buildSDK/dependancy_bootstrap.txt
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4848 <p>
4849 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4850
4851 <p>
4852 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String} name The attribute name
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (!this.dom.hasAttribute(name)) {
9582                 return undefined;
9583             }
9584             return this.dom.getAttribute(name);
9585         }
9586         
9587         
9588         
9589     };
9590
9591     var ep = El.prototype;
9592
9593     /**
9594      * Appends an event handler (Shorthand for addListener)
9595      * @param {String}   eventName     The type of event to append
9596      * @param {Function} fn        The method the event invokes
9597      * @param {Object} scope       (optional) The scope (this object) of the fn
9598      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9599      * @method
9600      */
9601     ep.on = ep.addListener;
9602         // backwards compat
9603     ep.mon = ep.addListener;
9604
9605     /**
9606      * Removes an event handler from this element (shorthand for removeListener)
9607      * @param {String} eventName the type of event to remove
9608      * @param {Function} fn the method the event invokes
9609      * @return {Roo.Element} this
9610      * @method
9611      */
9612     ep.un = ep.removeListener;
9613
9614     /**
9615      * true to automatically adjust width and height settings for box-model issues (default to true)
9616      */
9617     ep.autoBoxAdjust = true;
9618
9619     // private
9620     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9621
9622     // private
9623     El.addUnits = function(v, defaultUnit){
9624         if(v === "" || v == "auto"){
9625             return v;
9626         }
9627         if(v === undefined){
9628             return '';
9629         }
9630         if(typeof v == "number" || !El.unitPattern.test(v)){
9631             return v + (defaultUnit || 'px');
9632         }
9633         return v;
9634     };
9635
9636     // special markup used throughout Roo when box wrapping elements
9637     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9638     /**
9639      * Visibility mode constant - Use visibility to hide element
9640      * @static
9641      * @type Number
9642      */
9643     El.VISIBILITY = 1;
9644     /**
9645      * Visibility mode constant - Use display to hide element
9646      * @static
9647      * @type Number
9648      */
9649     El.DISPLAY = 2;
9650
9651     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9652     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9653     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9654
9655
9656
9657     /**
9658      * @private
9659      */
9660     El.cache = {};
9661
9662     var docEl;
9663
9664     /**
9665      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9666      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9667      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9668      * @return {Element} The Element object
9669      * @static
9670      */
9671     El.get = function(el){
9672         var ex, elm, id;
9673         if(!el){ return null; }
9674         if(typeof el == "string"){ // element id
9675             if(!(elm = document.getElementById(el))){
9676                 return null;
9677             }
9678             if(ex = El.cache[el]){
9679                 ex.dom = elm;
9680             }else{
9681                 ex = El.cache[el] = new El(elm);
9682             }
9683             return ex;
9684         }else if(el.tagName){ // dom element
9685             if(!(id = el.id)){
9686                 id = Roo.id(el);
9687             }
9688             if(ex = El.cache[id]){
9689                 ex.dom = el;
9690             }else{
9691                 ex = El.cache[id] = new El(el);
9692             }
9693             return ex;
9694         }else if(el instanceof El){
9695             if(el != docEl){
9696                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9697                                                               // catch case where it hasn't been appended
9698                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9699             }
9700             return el;
9701         }else if(el.isComposite){
9702             return el;
9703         }else if(el instanceof Array){
9704             return El.select(el);
9705         }else if(el == document){
9706             // create a bogus element object representing the document object
9707             if(!docEl){
9708                 var f = function(){};
9709                 f.prototype = El.prototype;
9710                 docEl = new f();
9711                 docEl.dom = document;
9712             }
9713             return docEl;
9714         }
9715         return null;
9716     };
9717
9718     // private
9719     El.uncache = function(el){
9720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9721             if(a[i]){
9722                 delete El.cache[a[i].id || a[i]];
9723             }
9724         }
9725     };
9726
9727     // private
9728     // Garbage collection - uncache elements/purge listeners on orphaned elements
9729     // so we don't hold a reference and cause the browser to retain them
9730     El.garbageCollect = function(){
9731         if(!Roo.enableGarbageCollector){
9732             clearInterval(El.collectorThread);
9733             return;
9734         }
9735         for(var eid in El.cache){
9736             var el = El.cache[eid], d = el.dom;
9737             // -------------------------------------------------------
9738             // Determining what is garbage:
9739             // -------------------------------------------------------
9740             // !d
9741             // dom node is null, definitely garbage
9742             // -------------------------------------------------------
9743             // !d.parentNode
9744             // no parentNode == direct orphan, definitely garbage
9745             // -------------------------------------------------------
9746             // !d.offsetParent && !document.getElementById(eid)
9747             // display none elements have no offsetParent so we will
9748             // also try to look it up by it's id. However, check
9749             // offsetParent first so we don't do unneeded lookups.
9750             // This enables collection of elements that are not orphans
9751             // directly, but somewhere up the line they have an orphan
9752             // parent.
9753             // -------------------------------------------------------
9754             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9755                 delete El.cache[eid];
9756                 if(d && Roo.enableListenerCollection){
9757                     E.purgeElement(d);
9758                 }
9759             }
9760         }
9761     }
9762     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9763
9764
9765     // dom is optional
9766     El.Flyweight = function(dom){
9767         this.dom = dom;
9768     };
9769     El.Flyweight.prototype = El.prototype;
9770
9771     El._flyweights = {};
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * @param {String/HTMLElement} el The dom node or id
9776      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9777      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9778      * @static
9779      * @return {Element} The shared Element object
9780      */
9781     El.fly = function(el, named){
9782         named = named || '_global';
9783         el = Roo.getDom(el);
9784         if(!el){
9785             return null;
9786         }
9787         if(!El._flyweights[named]){
9788             El._flyweights[named] = new El.Flyweight();
9789         }
9790         El._flyweights[named].dom = el;
9791         return El._flyweights[named];
9792     };
9793
9794     /**
9795      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9796      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9797      * Shorthand of {@link Roo.Element#get}
9798      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9799      * @return {Element} The Element object
9800      * @member Roo
9801      * @method get
9802      */
9803     Roo.get = El.get;
9804     /**
9805      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9806      * the dom node can be overwritten by other code.
9807      * Shorthand of {@link Roo.Element#fly}
9808      * @param {String/HTMLElement} el The dom node or id
9809      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9810      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9811      * @static
9812      * @return {Element} The shared Element object
9813      * @member Roo
9814      * @method fly
9815      */
9816     Roo.fly = El.fly;
9817
9818     // speedy lookup for elements never to box adjust
9819     var noBoxAdjust = Roo.isStrict ? {
9820         select:1
9821     } : {
9822         input:1, select:1, textarea:1
9823     };
9824     if(Roo.isIE || Roo.isGecko){
9825         noBoxAdjust['button'] = 1;
9826     }
9827
9828
9829     Roo.EventManager.on(window, 'unload', function(){
9830         delete El.cache;
9831         delete El._flyweights;
9832     });
9833 })();
9834
9835
9836
9837
9838 if(Roo.DomQuery){
9839     Roo.Element.selectorFunction = Roo.DomQuery.select;
9840 }
9841
9842 Roo.Element.select = function(selector, unique, root){
9843     var els;
9844     if(typeof selector == "string"){
9845         els = Roo.Element.selectorFunction(selector, root);
9846     }else if(selector.length !== undefined){
9847         els = selector;
9848     }else{
9849         throw "Invalid selector";
9850     }
9851     if(unique === true){
9852         return new Roo.CompositeElement(els);
9853     }else{
9854         return new Roo.CompositeElementLite(els);
9855     }
9856 };
9857 /**
9858  * Selects elements based on the passed CSS selector to enable working on them as 1.
9859  * @param {String/Array} selector The CSS selector or an array of elements
9860  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9861  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9862  * @return {CompositeElementLite/CompositeElement}
9863  * @member Roo
9864  * @method select
9865  */
9866 Roo.select = Roo.Element.select;
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881 /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893
9894 //Notifies Element that fx methods are available
9895 Roo.enableFx = true;
9896
9897 /**
9898  * @class Roo.Fx
9899  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9900  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9901  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9902  * Element effects to work.</p><br/>
9903  *
9904  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9905  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9906  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9907  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9908  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9909  * expected results and should be done with care.</p><br/>
9910  *
9911  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9912  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9913 <pre>
9914 Value  Description
9915 -----  -----------------------------
9916 tl     The top left corner
9917 t      The center of the top edge
9918 tr     The top right corner
9919 l      The center of the left edge
9920 r      The center of the right edge
9921 bl     The bottom left corner
9922 b      The center of the bottom edge
9923 br     The bottom right corner
9924 </pre>
9925  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9926  * below are common options that can be passed to any Fx method.</b>
9927  * @cfg {Function} callback A function called when the effect is finished
9928  * @cfg {Object} scope The scope of the effect function
9929  * @cfg {String} easing A valid Easing value for the effect
9930  * @cfg {String} afterCls A css class to apply after the effect
9931  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9932  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9933  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9934  * effects that end with the element being visually hidden, ignored otherwise)
9935  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9936  * a function which returns such a specification that will be applied to the Element after the effect finishes
9937  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9938  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9939  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9940  */
9941 Roo.Fx = {
9942         /**
9943          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9944          * origin for the slide effect.  This function automatically handles wrapping the element with
9945          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element in from the top
9949 el.slideIn();
9950
9951 // custom: slide the element in from the right with a 2-second duration
9952 el.slideIn('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideIn('t', {
9956     easing: 'easeOut',
9957     duration: .5
9958 });
9959 </code></pre>
9960          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9961          * @param {Object} options (optional) Object literal with any of the Fx config options
9962          * @return {Roo.Element} The Element
9963          */
9964     slideIn : function(anchor, o){
9965         var el = this.getFxEl();
9966         o = o || {};
9967
9968         el.queueFx(o, function(){
9969
9970             anchor = anchor || "t";
9971
9972             // fix display to visibility
9973             this.fixDisplay();
9974
9975             // restore values after effect
9976             var r = this.getFxRestore();
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "hidden");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             // clear out temp styles after slide and unwrap
9989             var after = function(){
9990                 el.fxUnwrap(wrap, r.pos, o);
9991                 st.width = r.width;
9992                 st.height = r.height;
9993                 el.afterFx(o);
9994             };
9995             // time to calc the positions
9996             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9997
9998             switch(anchor.toLowerCase()){
9999                 case "t":
10000                     wrap.setSize(b.width, 0);
10001                     st.left = st.bottom = "0";
10002                     a = {height: bh};
10003                 break;
10004                 case "l":
10005                     wrap.setSize(0, b.height);
10006                     st.right = st.top = "0";
10007                     a = {width: bw};
10008                 break;
10009                 case "r":
10010                     wrap.setSize(0, b.height);
10011                     wrap.setX(b.right);
10012                     st.left = st.top = "0";
10013                     a = {width: bw, points: pt};
10014                 break;
10015                 case "b":
10016                     wrap.setSize(b.width, 0);
10017                     wrap.setY(b.bottom);
10018                     st.left = st.top = "0";
10019                     a = {height: bh, points: pt};
10020                 break;
10021                 case "tl":
10022                     wrap.setSize(0, 0);
10023                     st.right = st.bottom = "0";
10024                     a = {width: bw, height: bh};
10025                 break;
10026                 case "bl":
10027                     wrap.setSize(0, 0);
10028                     wrap.setY(b.y+b.height);
10029                     st.right = st.top = "0";
10030                     a = {width: bw, height: bh, points: pt};
10031                 break;
10032                 case "br":
10033                     wrap.setSize(0, 0);
10034                     wrap.setXY([b.right, b.bottom]);
10035                     st.left = st.top = "0";
10036                     a = {width: bw, height: bh, points: pt};
10037                 break;
10038                 case "tr":
10039                     wrap.setSize(0, 0);
10040                     wrap.setX(b.x+b.width);
10041                     st.left = st.bottom = "0";
10042                     a = {width: bw, height: bh, points: pt};
10043                 break;
10044             }
10045             this.dom.style.visibility = "visible";
10046             wrap.show();
10047
10048             arguments.callee.anim = wrap.fxanim(a,
10049                 o,
10050                 'motion',
10051                 .5,
10052                 'easeOut', after);
10053         });
10054         return this;
10055     },
10056     
10057         /**
10058          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10059          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10060          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10061          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10062          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10063          * Usage:
10064          *<pre><code>
10065 // default: slide the element out to the top
10066 el.slideOut();
10067
10068 // custom: slide the element out to the right with a 2-second duration
10069 el.slideOut('r', { duration: 2 });
10070
10071 // common config options shown with default values
10072 el.slideOut('t', {
10073     easing: 'easeOut',
10074     duration: .5,
10075     remove: false,
10076     useDisplay: false
10077 });
10078 </code></pre>
10079          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10080          * @param {Object} options (optional) Object literal with any of the Fx config options
10081          * @return {Roo.Element} The Element
10082          */
10083     slideOut : function(anchor, o){
10084         var el = this.getFxEl();
10085         o = o || {};
10086
10087         el.queueFx(o, function(){
10088
10089             anchor = anchor || "t";
10090
10091             // restore values after effect
10092             var r = this.getFxRestore();
10093             
10094             var b = this.getBox();
10095             // fixed size for slide
10096             this.setSize(b);
10097
10098             // wrap if needed
10099             var wrap = this.fxWrap(r.pos, o, "visible");
10100
10101             var st = this.dom.style;
10102             st.visibility = "visible";
10103             st.position = "absolute";
10104
10105             wrap.setSize(b);
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.fxUnwrap(wrap, r.pos, o);
10115
10116                 st.width = r.width;
10117                 st.height = r.height;
10118
10119                 el.afterFx(o);
10120             };
10121
10122             var a, zero = {to: 0};
10123             switch(anchor.toLowerCase()){
10124                 case "t":
10125                     st.left = st.bottom = "0";
10126                     a = {height: zero};
10127                 break;
10128                 case "l":
10129                     st.right = st.top = "0";
10130                     a = {width: zero};
10131                 break;
10132                 case "r":
10133                     st.left = st.top = "0";
10134                     a = {width: zero, points: {to:[b.right, b.y]}};
10135                 break;
10136                 case "b":
10137                     st.left = st.top = "0";
10138                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "tl":
10141                     st.right = st.bottom = "0";
10142                     a = {width: zero, height: zero};
10143                 break;
10144                 case "bl":
10145                     st.right = st.top = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "br":
10149                     st.left = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10151                 break;
10152                 case "tr":
10153                     st.left = st.bottom = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10155                 break;
10156             }
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 "easeOut", after);
10163         });
10164         return this;
10165     },
10166
10167         /**
10168          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10169          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10170          * The element must be removed from the DOM using the 'remove' config option if desired.
10171          * Usage:
10172          *<pre><code>
10173 // default
10174 el.puff();
10175
10176 // common config options shown with default values
10177 el.puff({
10178     easing: 'easeOut',
10179     duration: .5,
10180     remove: false,
10181     useDisplay: false
10182 });
10183 </code></pre>
10184          * @param {Object} options (optional) Object literal with any of the Fx config options
10185          * @return {Roo.Element} The Element
10186          */
10187     puff : function(o){
10188         var el = this.getFxEl();
10189         o = o || {};
10190
10191         el.queueFx(o, function(){
10192             this.clearOpacity();
10193             this.show();
10194
10195             // restore values after effect
10196             var r = this.getFxRestore();
10197             var st = this.dom.style;
10198
10199             var after = function(){
10200                 if(o.useDisplay){
10201                     el.setDisplayed(false);
10202                 }else{
10203                     el.hide();
10204                 }
10205
10206                 el.clearOpacity();
10207
10208                 el.setPositioning(r.pos);
10209                 st.width = r.width;
10210                 st.height = r.height;
10211                 st.fontSize = '';
10212                 el.afterFx(o);
10213             };
10214
10215             var width = this.getWidth();
10216             var height = this.getHeight();
10217
10218             arguments.callee.anim = this.fxanim({
10219                     width : {to: this.adjustWidth(width * 2)},
10220                     height : {to: this.adjustHeight(height * 2)},
10221                     points : {by: [-(width * .5), -(height * .5)]},
10222                     opacity : {to: 0},
10223                     fontSize: {to:200, unit: "%"}
10224                 },
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10235          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10236          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.switchOff();
10241
10242 // all config options shown with default values
10243 el.switchOff({
10244     easing: 'easeIn',
10245     duration: .3,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     switchOff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.clip();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273                 el.setPositioning(r.pos);
10274                 st.width = r.width;
10275                 st.height = r.height;
10276
10277                 el.afterFx(o);
10278             };
10279
10280             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10281                 this.clearOpacity();
10282                 (function(){
10283                     this.fxanim({
10284                         height:{to:1},
10285                         points:{by:[0, this.getHeight() * .5]}
10286                     }, o, 'motion', 0.3, 'easeIn', after);
10287                 }).defer(100, this);
10288             });
10289         });
10290         return this;
10291     },
10292
10293     /**
10294      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10295      * changed using the "attr" config option) and then fading back to the original color. If no original
10296      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10297      * Usage:
10298 <pre><code>
10299 // default: highlight background to yellow
10300 el.highlight();
10301
10302 // custom: highlight foreground text to blue for 2 seconds
10303 el.highlight("0000ff", { attr: 'color', duration: 2 });
10304
10305 // common config options shown with default values
10306 el.highlight("ffff9c", {
10307     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10308     endColor: (current color) or "ffffff",
10309     easing: 'easeIn',
10310     duration: 1
10311 });
10312 </code></pre>
10313      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10314      * @param {Object} options (optional) Object literal with any of the Fx config options
10315      * @return {Roo.Element} The Element
10316      */ 
10317     highlight : function(color, o){
10318         var el = this.getFxEl();
10319         o = o || {};
10320
10321         el.queueFx(o, function(){
10322             color = color || "ffff9c";
10323             attr = o.attr || "backgroundColor";
10324
10325             this.clearOpacity();
10326             this.show();
10327
10328             var origColor = this.getColor(attr);
10329             var restoreColor = this.dom.style[attr];
10330             endColor = (o.endColor || origColor) || "ffffff";
10331
10332             var after = function(){
10333                 el.dom.style[attr] = restoreColor;
10334                 el.afterFx(o);
10335             };
10336
10337             var a = {};
10338             a[attr] = {from: color, to: endColor};
10339             arguments.callee.anim = this.fxanim(a,
10340                 o,
10341                 'color',
10342                 1,
10343                 'easeIn', after);
10344         });
10345         return this;
10346     },
10347
10348    /**
10349     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10350     * Usage:
10351 <pre><code>
10352 // default: a single light blue ripple
10353 el.frame();
10354
10355 // custom: 3 red ripples lasting 3 seconds total
10356 el.frame("ff0000", 3, { duration: 3 });
10357
10358 // common config options shown with default values
10359 el.frame("C3DAF9", 1, {
10360     duration: 1 //duration of entire animation (not each individual ripple)
10361     // Note: Easing is not configurable and will be ignored if included
10362 });
10363 </code></pre>
10364     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10365     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10366     * @param {Object} options (optional) Object literal with any of the Fx config options
10367     * @return {Roo.Element} The Element
10368     */
10369     frame : function(color, count, o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             color = color || "#C3DAF9";
10375             if(color.length == 6){
10376                 color = "#" + color;
10377             }
10378             count = count || 1;
10379             duration = o.duration || 1;
10380             this.show();
10381
10382             var b = this.getBox();
10383             var animFn = function(){
10384                 var proxy = this.createProxy({
10385
10386                      style:{
10387                         visbility:"hidden",
10388                         position:"absolute",
10389                         "z-index":"35000", // yee haw
10390                         border:"0px solid " + color
10391                      }
10392                   });
10393                 var scale = Roo.isBorderBox ? 2 : 1;
10394                 proxy.animate({
10395                     top:{from:b.y, to:b.y - 20},
10396                     left:{from:b.x, to:b.x - 20},
10397                     borderWidth:{from:0, to:10},
10398                     opacity:{from:1, to:0},
10399                     height:{from:b.height, to:(b.height + (20*scale))},
10400                     width:{from:b.width, to:(b.width + (20*scale))}
10401                 }, duration, function(){
10402                     proxy.remove();
10403                 });
10404                 if(--count > 0){
10405                      animFn.defer((duration/2)*1000, this);
10406                 }else{
10407                     el.afterFx(o);
10408                 }
10409             };
10410             animFn.call(this);
10411         });
10412         return this;
10413     },
10414
10415    /**
10416     * Creates a pause before any subsequent queued effects begin.  If there are
10417     * no effects queued after the pause it will have no effect.
10418     * Usage:
10419 <pre><code>
10420 el.pause(1);
10421 </code></pre>
10422     * @param {Number} seconds The length of time to pause (in seconds)
10423     * @return {Roo.Element} The Element
10424     */
10425     pause : function(seconds){
10426         var el = this.getFxEl();
10427         var o = {};
10428
10429         el.queueFx(o, function(){
10430             setTimeout(function(){
10431                 el.afterFx(o);
10432             }, seconds * 1000);
10433         });
10434         return this;
10435     },
10436
10437    /**
10438     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10439     * using the "endOpacity" config option.
10440     * Usage:
10441 <pre><code>
10442 // default: fade in from opacity 0 to 100%
10443 el.fadeIn();
10444
10445 // custom: fade in from opacity 0 to 75% over 2 seconds
10446 el.fadeIn({ endOpacity: .75, duration: 2});
10447
10448 // common config options shown with default values
10449 el.fadeIn({
10450     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10451     easing: 'easeOut',
10452     duration: .5
10453 });
10454 </code></pre>
10455     * @param {Object} options (optional) Object literal with any of the Fx config options
10456     * @return {Roo.Element} The Element
10457     */
10458     fadeIn : function(o){
10459         var el = this.getFxEl();
10460         o = o || {};
10461         el.queueFx(o, function(){
10462             this.setOpacity(0);
10463             this.fixDisplay();
10464             this.dom.style.visibility = 'visible';
10465             var to = o.endOpacity || 1;
10466             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10467                 o, null, .5, "easeOut", function(){
10468                 if(to == 1){
10469                     this.clearOpacity();
10470                 }
10471                 el.afterFx(o);
10472             });
10473         });
10474         return this;
10475     },
10476
10477    /**
10478     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10479     * using the "endOpacity" config option.
10480     * Usage:
10481 <pre><code>
10482 // default: fade out from the element's current opacity to 0
10483 el.fadeOut();
10484
10485 // custom: fade out from the element's current opacity to 25% over 2 seconds
10486 el.fadeOut({ endOpacity: .25, duration: 2});
10487
10488 // common config options shown with default values
10489 el.fadeOut({
10490     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10491     easing: 'easeOut',
10492     duration: .5
10493     remove: false,
10494     useDisplay: false
10495 });
10496 </code></pre>
10497     * @param {Object} options (optional) Object literal with any of the Fx config options
10498     * @return {Roo.Element} The Element
10499     */
10500     fadeOut : function(o){
10501         var el = this.getFxEl();
10502         o = o || {};
10503         el.queueFx(o, function(){
10504             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10505                 o, null, .5, "easeOut", function(){
10506                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10507                      this.dom.style.display = "none";
10508                 }else{
10509                      this.dom.style.visibility = "hidden";
10510                 }
10511                 this.clearOpacity();
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Animates the transition of an element's dimensions from a starting height/width
10520     * to an ending height/width.
10521     * Usage:
10522 <pre><code>
10523 // change height and width to 100x100 pixels
10524 el.scale(100, 100);
10525
10526 // common config options shown with default values.  The height and width will default to
10527 // the element's existing values if passed as null.
10528 el.scale(
10529     [element's width],
10530     [element's height], {
10531     easing: 'easeOut',
10532     duration: .35
10533 });
10534 </code></pre>
10535     * @param {Number} width  The new width (pass undefined to keep the original width)
10536     * @param {Number} height  The new height (pass undefined to keep the original height)
10537     * @param {Object} options (optional) Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     scale : function(w, h, o){
10541         this.shift(Roo.apply({}, o, {
10542             width: w,
10543             height: h
10544         }));
10545         return this;
10546     },
10547
10548    /**
10549     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10550     * Any of these properties not specified in the config object will not be changed.  This effect 
10551     * requires that at least one new dimension, position or opacity setting must be passed in on
10552     * the config object in order for the function to have any effect.
10553     * Usage:
10554 <pre><code>
10555 // slide the element horizontally to x position 200 while changing the height and opacity
10556 el.shift({ x: 200, height: 50, opacity: .8 });
10557
10558 // common config options shown with default values.
10559 el.shift({
10560     width: [element's width],
10561     height: [element's height],
10562     x: [element's x position],
10563     y: [element's y position],
10564     opacity: [element's opacity],
10565     easing: 'easeOut',
10566     duration: .35
10567 });
10568 </code></pre>
10569     * @param {Object} options  Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     shift : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10577             if(w !== undefined){
10578                 a.width = {to: this.adjustWidth(w)};
10579             }
10580             if(h !== undefined){
10581                 a.height = {to: this.adjustHeight(h)};
10582             }
10583             if(x !== undefined || y !== undefined){
10584                 a.points = {to: [
10585                     x !== undefined ? x : this.getX(),
10586                     y !== undefined ? y : this.getY()
10587                 ]};
10588             }
10589             if(op !== undefined){
10590                 a.opacity = {to: op};
10591             }
10592             if(o.xy !== undefined){
10593                 a.points = {to: o.xy};
10594             }
10595             arguments.callee.anim = this.fxanim(a,
10596                 o, 'motion', .35, "easeOut", function(){
10597                 el.afterFx(o);
10598             });
10599         });
10600         return this;
10601     },
10602
10603         /**
10604          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10605          * ending point of the effect.
10606          * Usage:
10607          *<pre><code>
10608 // default: slide the element downward while fading out
10609 el.ghost();
10610
10611 // custom: slide the element out to the right with a 2-second duration
10612 el.ghost('r', { duration: 2 });
10613
10614 // common config options shown with default values
10615 el.ghost('b', {
10616     easing: 'easeOut',
10617     duration: .5
10618     remove: false,
10619     useDisplay: false
10620 });
10621 </code></pre>
10622          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10623          * @param {Object} options (optional) Object literal with any of the Fx config options
10624          * @return {Roo.Element} The Element
10625          */
10626     ghost : function(anchor, o){
10627         var el = this.getFxEl();
10628         o = o || {};
10629
10630         el.queueFx(o, function(){
10631             anchor = anchor || "b";
10632
10633             // restore values after effect
10634             var r = this.getFxRestore();
10635             var w = this.getWidth(),
10636                 h = this.getHeight();
10637
10638             var st = this.dom.style;
10639
10640             var after = function(){
10641                 if(o.useDisplay){
10642                     el.setDisplayed(false);
10643                 }else{
10644                     el.hide();
10645                 }
10646
10647                 el.clearOpacity();
10648                 el.setPositioning(r.pos);
10649                 st.width = r.width;
10650                 st.height = r.height;
10651
10652                 el.afterFx(o);
10653             };
10654
10655             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10656             switch(anchor.toLowerCase()){
10657                 case "t":
10658                     pt.by = [0, -h];
10659                 break;
10660                 case "l":
10661                     pt.by = [-w, 0];
10662                 break;
10663                 case "r":
10664                     pt.by = [w, 0];
10665                 break;
10666                 case "b":
10667                     pt.by = [0, h];
10668                 break;
10669                 case "tl":
10670                     pt.by = [-w, -h];
10671                 break;
10672                 case "bl":
10673                     pt.by = [-w, h];
10674                 break;
10675                 case "br":
10676                     pt.by = [w, h];
10677                 break;
10678                 case "tr":
10679                     pt.by = [w, -h];
10680                 break;
10681             }
10682
10683             arguments.callee.anim = this.fxanim(a,
10684                 o,
10685                 'motion',
10686                 .5,
10687                 "easeOut", after);
10688         });
10689         return this;
10690     },
10691
10692         /**
10693          * Ensures that all effects queued after syncFx is called on the element are
10694          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10695          * @return {Roo.Element} The Element
10696          */
10697     syncFx : function(){
10698         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10699             block : false,
10700             concurrent : true,
10701             stopFx : false
10702         });
10703         return this;
10704     },
10705
10706         /**
10707          * Ensures that all effects queued after sequenceFx is called on the element are
10708          * run in sequence.  This is the opposite of {@link #syncFx}.
10709          * @return {Roo.Element} The Element
10710          */
10711     sequenceFx : function(){
10712         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10713             block : false,
10714             concurrent : false,
10715             stopFx : false
10716         });
10717         return this;
10718     },
10719
10720         /* @private */
10721     nextFx : function(){
10722         var ef = this.fxQueue[0];
10723         if(ef){
10724             ef.call(this);
10725         }
10726     },
10727
10728         /**
10729          * Returns true if the element has any effects actively running or queued, else returns false.
10730          * @return {Boolean} True if element has active effects, else false
10731          */
10732     hasActiveFx : function(){
10733         return this.fxQueue && this.fxQueue[0];
10734     },
10735
10736         /**
10737          * Stops any running effects and clears the element's internal effects queue if it contains
10738          * any additional effects that haven't started yet.
10739          * @return {Roo.Element} The Element
10740          */
10741     stopFx : function(){
10742         if(this.hasActiveFx()){
10743             var cur = this.fxQueue[0];
10744             if(cur && cur.anim && cur.anim.isAnimated()){
10745                 this.fxQueue = [cur]; // clear out others
10746                 cur.anim.stop(true);
10747             }
10748         }
10749         return this;
10750     },
10751
10752         /* @private */
10753     beforeFx : function(o){
10754         if(this.hasActiveFx() && !o.concurrent){
10755            if(o.stopFx){
10756                this.stopFx();
10757                return true;
10758            }
10759            return false;
10760         }
10761         return true;
10762     },
10763
10764         /**
10765          * Returns true if the element is currently blocking so that no other effect can be queued
10766          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10767          * used to ensure that an effect initiated by a user action runs to completion prior to the
10768          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10769          * @return {Boolean} True if blocking, else false
10770          */
10771     hasFxBlock : function(){
10772         var q = this.fxQueue;
10773         return q && q[0] && q[0].block;
10774     },
10775
10776         /* @private */
10777     queueFx : function(o, fn){
10778         if(!this.fxQueue){
10779             this.fxQueue = [];
10780         }
10781         if(!this.hasFxBlock()){
10782             Roo.applyIf(o, this.fxDefaults);
10783             if(!o.concurrent){
10784                 var run = this.beforeFx(o);
10785                 fn.block = o.block;
10786                 this.fxQueue.push(fn);
10787                 if(run){
10788                     this.nextFx();
10789                 }
10790             }else{
10791                 fn.call(this);
10792             }
10793         }
10794         return this;
10795     },
10796
10797         /* @private */
10798     fxWrap : function(pos, o, vis){
10799         var wrap;
10800         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10801             var wrapXY;
10802             if(o.fixPosition){
10803                 wrapXY = this.getXY();
10804             }
10805             var div = document.createElement("div");
10806             div.style.visibility = vis;
10807             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10808             wrap.setPositioning(pos);
10809             if(wrap.getStyle("position") == "static"){
10810                 wrap.position("relative");
10811             }
10812             this.clearPositioning('auto');
10813             wrap.clip();
10814             wrap.dom.appendChild(this.dom);
10815             if(wrapXY){
10816                 wrap.setXY(wrapXY);
10817             }
10818         }
10819         return wrap;
10820     },
10821
10822         /* @private */
10823     fxUnwrap : function(wrap, pos, o){
10824         this.clearPositioning();
10825         this.setPositioning(pos);
10826         if(!o.wrap){
10827             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10828             wrap.remove();
10829         }
10830     },
10831
10832         /* @private */
10833     getFxRestore : function(){
10834         var st = this.dom.style;
10835         return {pos: this.getPositioning(), width: st.width, height : st.height};
10836     },
10837
10838         /* @private */
10839     afterFx : function(o){
10840         if(o.afterStyle){
10841             this.applyStyles(o.afterStyle);
10842         }
10843         if(o.afterCls){
10844             this.addClass(o.afterCls);
10845         }
10846         if(o.remove === true){
10847             this.remove();
10848         }
10849         Roo.callback(o.callback, o.scope, [this]);
10850         if(!o.concurrent){
10851             this.fxQueue.shift();
10852             this.nextFx();
10853         }
10854     },
10855
10856         /* @private */
10857     getFxEl : function(){ // support for composite element fx
10858         return Roo.get(this.dom);
10859     },
10860
10861         /* @private */
10862     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10863         animType = animType || 'run';
10864         opt = opt || {};
10865         var anim = Roo.lib.Anim[animType](
10866             this.dom, args,
10867             (opt.duration || defaultDur) || .35,
10868             (opt.easing || defaultEase) || 'easeOut',
10869             function(){
10870                 Roo.callback(cb, this);
10871             },
10872             this
10873         );
10874         opt.anim = anim;
10875         return anim;
10876     }
10877 };
10878
10879 // backwords compat
10880 Roo.Fx.resize = Roo.Fx.scale;
10881
10882 //When included, Roo.Fx is automatically applied to Element so that all basic
10883 //effects are available directly via the Element API
10884 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10885  * Based on:
10886  * Ext JS Library 1.1.1
10887  * Copyright(c) 2006-2007, Ext JS, LLC.
10888  *
10889  * Originally Released Under LGPL - original licence link has changed is not relivant.
10890  *
10891  * Fork - LGPL
10892  * <script type="text/javascript">
10893  */
10894
10895
10896 /**
10897  * @class Roo.CompositeElement
10898  * Standard composite class. Creates a Roo.Element for every element in the collection.
10899  * <br><br>
10900  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10901  * actions will be performed on all the elements in this collection.</b>
10902  * <br><br>
10903  * All methods return <i>this</i> and can be chained.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class", true);
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class', true);
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre>
10915  */
10916 Roo.CompositeElement = function(els){
10917     this.elements = [];
10918     this.addElements(els);
10919 };
10920 Roo.CompositeElement.prototype = {
10921     isComposite: true,
10922     addElements : function(els){
10923         if(!els) return this;
10924         if(typeof els == "string"){
10925             els = Roo.Element.selectorFunction(els);
10926         }
10927         var yels = this.elements;
10928         var index = yels.length-1;
10929         for(var i = 0, len = els.length; i < len; i++) {
10930                 yels[++index] = Roo.get(els[i]);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Clears this composite and adds the elements returned by the passed selector.
10937     * @param {String/Array} els A string CSS selector, an array of elements or an element
10938     * @return {CompositeElement} this
10939     */
10940     fill : function(els){
10941         this.elements = [];
10942         this.add(els);
10943         return this;
10944     },
10945
10946     /**
10947     * Filters this composite to only elements that match the passed selector.
10948     * @param {String} selector A string CSS selector
10949     * @return {CompositeElement} this
10950     */
10951     filter : function(selector){
10952         var els = [];
10953         this.each(function(el){
10954             if(el.is(selector)){
10955                 els[els.length] = el.dom;
10956             }
10957         });
10958         this.fill(els);
10959         return this;
10960     },
10961
10962     invoke : function(fn, args){
10963         var els = this.elements;
10964         for(var i = 0, len = els.length; i < len; i++) {
10965                 Roo.Element.prototype[fn].apply(els[i], args);
10966         }
10967         return this;
10968     },
10969     /**
10970     * Adds elements to this composite.
10971     * @param {String/Array} els A string CSS selector, an array of elements or an element
10972     * @return {CompositeElement} this
10973     */
10974     add : function(els){
10975         if(typeof els == "string"){
10976             this.addElements(Roo.Element.selectorFunction(els));
10977         }else if(els.length !== undefined){
10978             this.addElements(els);
10979         }else{
10980             this.addElements([els]);
10981         }
10982         return this;
10983     },
10984     /**
10985     * Calls the passed function passing (el, this, index) for each element in this composite.
10986     * @param {Function} fn The function to call
10987     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10988     * @return {CompositeElement} this
10989     */
10990     each : function(fn, scope){
10991         var els = this.elements;
10992         for(var i = 0, len = els.length; i < len; i++){
10993             if(fn.call(scope || els[i], els[i], this, i) === false) {
10994                 break;
10995             }
10996         }
10997         return this;
10998     },
10999
11000     /**
11001      * Returns the Element object at the specified index
11002      * @param {Number} index
11003      * @return {Roo.Element}
11004      */
11005     item : function(index){
11006         return this.elements[index] || null;
11007     },
11008
11009     /**
11010      * Returns the first Element
11011      * @return {Roo.Element}
11012      */
11013     first : function(){
11014         return this.item(0);
11015     },
11016
11017     /**
11018      * Returns the last Element
11019      * @return {Roo.Element}
11020      */
11021     last : function(){
11022         return this.item(this.elements.length-1);
11023     },
11024
11025     /**
11026      * Returns the number of elements in this composite
11027      * @return Number
11028      */
11029     getCount : function(){
11030         return this.elements.length;
11031     },
11032
11033     /**
11034      * Returns true if this composite contains the passed element
11035      * @return Boolean
11036      */
11037     contains : function(el){
11038         return this.indexOf(el) !== -1;
11039     },
11040
11041     /**
11042      * Returns true if this composite contains the passed element
11043      * @return Boolean
11044      */
11045     indexOf : function(el){
11046         return this.elements.indexOf(Roo.get(el));
11047     },
11048
11049
11050     /**
11051     * Removes the specified element(s).
11052     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11053     * or an array of any of those.
11054     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11055     * @return {CompositeElement} this
11056     */
11057     removeElement : function(el, removeDom){
11058         if(el instanceof Array){
11059             for(var i = 0, len = el.length; i < len; i++){
11060                 this.removeElement(el[i]);
11061             }
11062             return this;
11063         }
11064         var index = typeof el == 'number' ? el : this.indexOf(el);
11065         if(index !== -1){
11066             if(removeDom){
11067                 var d = this.elements[index];
11068                 if(d.dom){
11069                     d.remove();
11070                 }else{
11071                     d.parentNode.removeChild(d);
11072                 }
11073             }
11074             this.elements.splice(index, 1);
11075         }
11076         return this;
11077     },
11078
11079     /**
11080     * Replaces the specified element with the passed element.
11081     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11082     * to replace.
11083     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11084     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11085     * @return {CompositeElement} this
11086     */
11087     replaceElement : function(el, replacement, domReplace){
11088         var index = typeof el == 'number' ? el : this.indexOf(el);
11089         if(index !== -1){
11090             if(domReplace){
11091                 this.elements[index].replaceWith(replacement);
11092             }else{
11093                 this.elements.splice(index, 1, Roo.get(replacement))
11094             }
11095         }
11096         return this;
11097     },
11098
11099     /**
11100      * Removes all elements.
11101      */
11102     clear : function(){
11103         this.elements = [];
11104     }
11105 };
11106 (function(){
11107     Roo.CompositeElement.createCall = function(proto, fnName){
11108         if(!proto[fnName]){
11109             proto[fnName] = function(){
11110                 return this.invoke(fnName, arguments);
11111             };
11112         }
11113     };
11114     for(var fnName in Roo.Element.prototype){
11115         if(typeof Roo.Element.prototype[fnName] == "function"){
11116             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11117         }
11118     };
11119 })();
11120 /*
11121  * Based on:
11122  * Ext JS Library 1.1.1
11123  * Copyright(c) 2006-2007, Ext JS, LLC.
11124  *
11125  * Originally Released Under LGPL - original licence link has changed is not relivant.
11126  *
11127  * Fork - LGPL
11128  * <script type="text/javascript">
11129  */
11130
11131 /**
11132  * @class Roo.CompositeElementLite
11133  * @extends Roo.CompositeElement
11134  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11135  <pre><code>
11136  var els = Roo.select("#some-el div.some-class");
11137  // or select directly from an existing element
11138  var el = Roo.get('some-el');
11139  el.select('div.some-class');
11140
11141  els.setWidth(100); // all elements become 100 width
11142  els.hide(true); // all elements fade out and hide
11143  // or
11144  els.setWidth(100).hide(true);
11145  </code></pre><br><br>
11146  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11147  * actions will be performed on all the elements in this collection.</b>
11148  */
11149 Roo.CompositeElementLite = function(els){
11150     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11151     this.el = new Roo.Element.Flyweight();
11152 };
11153 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11154     addElements : function(els){
11155         if(els){
11156             if(els instanceof Array){
11157                 this.elements = this.elements.concat(els);
11158             }else{
11159                 var yels = this.elements;
11160                 var index = yels.length-1;
11161                 for(var i = 0, len = els.length; i < len; i++) {
11162                     yels[++index] = els[i];
11163                 }
11164             }
11165         }
11166         return this;
11167     },
11168     invoke : function(fn, args){
11169         var els = this.elements;
11170         var el = this.el;
11171         for(var i = 0, len = els.length; i < len; i++) {
11172             el.dom = els[i];
11173                 Roo.Element.prototype[fn].apply(el, args);
11174         }
11175         return this;
11176     },
11177     /**
11178      * Returns a flyweight Element of the dom element object at the specified index
11179      * @param {Number} index
11180      * @return {Roo.Element}
11181      */
11182     item : function(index){
11183         if(!this.elements[index]){
11184             return null;
11185         }
11186         this.el.dom = this.elements[index];
11187         return this.el;
11188     },
11189
11190     // fixes scope with flyweight
11191     addListener : function(eventName, handler, scope, opt){
11192         var els = this.elements;
11193         for(var i = 0, len = els.length; i < len; i++) {
11194             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11195         }
11196         return this;
11197     },
11198
11199     /**
11200     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11201     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11202     * a reference to the dom node, use el.dom.</b>
11203     * @param {Function} fn The function to call
11204     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11205     * @return {CompositeElement} this
11206     */
11207     each : function(fn, scope){
11208         var els = this.elements;
11209         var el = this.el;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             el.dom = els[i];
11212                 if(fn.call(scope || el, el, this, i) === false){
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     indexOf : function(el){
11220         return this.elements.indexOf(Roo.getDom(el));
11221     },
11222
11223     replaceElement : function(el, replacement, domReplace){
11224         var index = typeof el == 'number' ? el : this.indexOf(el);
11225         if(index !== -1){
11226             replacement = Roo.getDom(replacement);
11227             if(domReplace){
11228                 var d = this.elements[index];
11229                 d.parentNode.insertBefore(replacement, d);
11230                 d.parentNode.removeChild(d);
11231             }
11232             this.elements.splice(index, 1, replacement);
11233         }
11234         return this;
11235     }
11236 });
11237 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11238
11239 /*
11240  * Based on:
11241  * Ext JS Library 1.1.1
11242  * Copyright(c) 2006-2007, Ext JS, LLC.
11243  *
11244  * Originally Released Under LGPL - original licence link has changed is not relivant.
11245  *
11246  * Fork - LGPL
11247  * <script type="text/javascript">
11248  */
11249
11250  
11251
11252 /**
11253  * @class Roo.data.Connection
11254  * @extends Roo.util.Observable
11255  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11256  * either to a configured URL, or to a URL specified at request time.<br><br>
11257  * <p>
11258  * Requests made by this class are asynchronous, and will return immediately. No data from
11259  * the server will be available to the statement immediately following the {@link #request} call.
11260  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11261  * <p>
11262  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11263  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11264  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11265  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11266  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11267  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11268  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11269  * standard DOM methods.
11270  * @constructor
11271  * @param {Object} config a configuration object.
11272  */
11273 Roo.data.Connection = function(config){
11274     Roo.apply(this, config);
11275     this.addEvents({
11276         /**
11277          * @event beforerequest
11278          * Fires before a network request is made to retrieve a data object.
11279          * @param {Connection} conn This Connection object.
11280          * @param {Object} options The options config object passed to the {@link #request} method.
11281          */
11282         "beforerequest" : true,
11283         /**
11284          * @event requestcomplete
11285          * Fires if the request was successfully completed.
11286          * @param {Connection} conn This Connection object.
11287          * @param {Object} response The XHR object containing the response data.
11288          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11289          * @param {Object} options The options config object passed to the {@link #request} method.
11290          */
11291         "requestcomplete" : true,
11292         /**
11293          * @event requestexception
11294          * Fires if an error HTTP status was returned from the server.
11295          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11296          * @param {Connection} conn This Connection object.
11297          * @param {Object} response The XHR object containing the response data.
11298          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11299          * @param {Object} options The options config object passed to the {@link #request} method.
11300          */
11301         "requestexception" : true
11302     });
11303     Roo.data.Connection.superclass.constructor.call(this);
11304 };
11305
11306 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11307     /**
11308      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11309      */
11310     /**
11311      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11312      * extra parameters to each request made by this object. (defaults to undefined)
11313      */
11314     /**
11315      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11316      *  to each request made by this object. (defaults to undefined)
11317      */
11318     /**
11319      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11320      */
11321     /**
11322      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11323      */
11324     timeout : 30000,
11325     /**
11326      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11327      * @type Boolean
11328      */
11329     autoAbort:false,
11330
11331     /**
11332      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11333      * @type Boolean
11334      */
11335     disableCaching: true,
11336
11337     /**
11338      * Sends an HTTP request to a remote server.
11339      * @param {Object} options An object which may contain the following properties:<ul>
11340      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11341      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11342      * request, a url encoded string or a function to call to get either.</li>
11343      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11344      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11345      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11346      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11347      * <li>options {Object} The parameter to the request call.</li>
11348      * <li>success {Boolean} True if the request succeeded.</li>
11349      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11350      * </ul></li>
11351      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11352      * The callback is passed the following parameters:<ul>
11353      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11354      * <li>options {Object} The parameter to the request call.</li>
11355      * </ul></li>
11356      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11357      * The callback is passed the following parameters:<ul>
11358      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11359      * <li>options {Object} The parameter to the request call.</li>
11360      * </ul></li>
11361      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11362      * for the callback function. Defaults to the browser window.</li>
11363      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11364      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11365      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11366      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11367      * params for the post data. Any params will be appended to the URL.</li>
11368      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11369      * </ul>
11370      * @return {Number} transactionId
11371      */
11372     request : function(o){
11373         if(this.fireEvent("beforerequest", this, o) !== false){
11374             var p = o.params;
11375
11376             if(typeof p == "function"){
11377                 p = p.call(o.scope||window, o);
11378             }
11379             if(typeof p == "object"){
11380                 p = Roo.urlEncode(o.params);
11381             }
11382             if(this.extraParams){
11383                 var extras = Roo.urlEncode(this.extraParams);
11384                 p = p ? (p + '&' + extras) : extras;
11385             }
11386
11387             var url = o.url || this.url;
11388             if(typeof url == 'function'){
11389                 url = url.call(o.scope||window, o);
11390             }
11391
11392             if(o.form){
11393                 var form = Roo.getDom(o.form);
11394                 url = url || form.action;
11395
11396                 var enctype = form.getAttribute("enctype");
11397                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11398                     return this.doFormUpload(o, p, url);
11399                 }
11400                 var f = Roo.lib.Ajax.serializeForm(form);
11401                 p = p ? (p + '&' + f) : f;
11402             }
11403
11404             var hs = o.headers;
11405             if(this.defaultHeaders){
11406                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11407                 if(!o.headers){
11408                     o.headers = hs;
11409                 }
11410             }
11411
11412             var cb = {
11413                 success: this.handleResponse,
11414                 failure: this.handleFailure,
11415                 scope: this,
11416                 argument: {options: o},
11417                 timeout : o.timeout || this.timeout
11418             };
11419
11420             var method = o.method||this.method||(p ? "POST" : "GET");
11421
11422             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11423                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11424             }
11425
11426             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11427                 if(o.autoAbort){
11428                     this.abort();
11429                 }
11430             }else if(this.autoAbort !== false){
11431                 this.abort();
11432             }
11433
11434             if((method == 'GET' && p) || o.xmlData){
11435                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11436                 p = '';
11437             }
11438             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11439             return this.transId;
11440         }else{
11441             Roo.callback(o.callback, o.scope, [o, null, null]);
11442             return null;
11443         }
11444     },
11445
11446     /**
11447      * Determine whether this object has a request outstanding.
11448      * @param {Number} transactionId (Optional) defaults to the last transaction
11449      * @return {Boolean} True if there is an outstanding request.
11450      */
11451     isLoading : function(transId){
11452         if(transId){
11453             return Roo.lib.Ajax.isCallInProgress(transId);
11454         }else{
11455             return this.transId ? true : false;
11456         }
11457     },
11458
11459     /**
11460      * Aborts any outstanding request.
11461      * @param {Number} transactionId (Optional) defaults to the last transaction
11462      */
11463     abort : function(transId){
11464         if(transId || this.isLoading()){
11465             Roo.lib.Ajax.abort(transId || this.transId);
11466         }
11467     },
11468
11469     // private
11470     handleResponse : function(response){
11471         this.transId = false;
11472         var options = response.argument.options;
11473         response.argument = options ? options.argument : null;
11474         this.fireEvent("requestcomplete", this, response, options);
11475         Roo.callback(options.success, options.scope, [response, options]);
11476         Roo.callback(options.callback, options.scope, [options, true, response]);
11477     },
11478
11479     // private
11480     handleFailure : function(response, e){
11481         this.transId = false;
11482         var options = response.argument.options;
11483         response.argument = options ? options.argument : null;
11484         this.fireEvent("requestexception", this, response, options, e);
11485         Roo.callback(options.failure, options.scope, [response, options]);
11486         Roo.callback(options.callback, options.scope, [options, false, response]);
11487     },
11488
11489     // private
11490     doFormUpload : function(o, ps, url){
11491         var id = Roo.id();
11492         var frame = document.createElement('iframe');
11493         frame.id = id;
11494         frame.name = id;
11495         frame.className = 'x-hidden';
11496         if(Roo.isIE){
11497             frame.src = Roo.SSL_SECURE_URL;
11498         }
11499         document.body.appendChild(frame);
11500
11501         if(Roo.isIE){
11502            document.frames[id].name = id;
11503         }
11504
11505         var form = Roo.getDom(o.form);
11506         form.target = id;
11507         form.method = 'POST';
11508         form.enctype = form.encoding = 'multipart/form-data';
11509         if(url){
11510             form.action = url;
11511         }
11512
11513         var hiddens, hd;
11514         if(ps){ // add dynamic params
11515             hiddens = [];
11516             ps = Roo.urlDecode(ps, false);
11517             for(var k in ps){
11518                 if(ps.hasOwnProperty(k)){
11519                     hd = document.createElement('input');
11520                     hd.type = 'hidden';
11521                     hd.name = k;
11522                     hd.value = ps[k];
11523                     form.appendChild(hd);
11524                     hiddens.push(hd);
11525                 }
11526             }
11527         }
11528
11529         function cb(){
11530             var r = {  // bogus response object
11531                 responseText : '',
11532                 responseXML : null
11533             };
11534
11535             r.argument = o ? o.argument : null;
11536
11537             try { //
11538                 var doc;
11539                 if(Roo.isIE){
11540                     doc = frame.contentWindow.document;
11541                 }else {
11542                     doc = (frame.contentDocument || window.frames[id].document);
11543                 }
11544                 if(doc && doc.body){
11545                     r.responseText = doc.body.innerHTML;
11546                 }
11547                 if(doc && doc.XMLDocument){
11548                     r.responseXML = doc.XMLDocument;
11549                 }else {
11550                     r.responseXML = doc;
11551                 }
11552             }
11553             catch(e) {
11554                 // ignore
11555             }
11556
11557             Roo.EventManager.removeListener(frame, 'load', cb, this);
11558
11559             this.fireEvent("requestcomplete", this, r, o);
11560             Roo.callback(o.success, o.scope, [r, o]);
11561             Roo.callback(o.callback, o.scope, [o, true, r]);
11562
11563             setTimeout(function(){document.body.removeChild(frame);}, 100);
11564         }
11565
11566         Roo.EventManager.on(frame, 'load', cb, this);
11567         form.submit();
11568
11569         if(hiddens){ // remove dynamic params
11570             for(var i = 0, len = hiddens.length; i < len; i++){
11571                 form.removeChild(hiddens[i]);
11572             }
11573         }
11574     }
11575 });
11576 /*
11577  * Based on:
11578  * Ext JS Library 1.1.1
11579  * Copyright(c) 2006-2007, Ext JS, LLC.
11580  *
11581  * Originally Released Under LGPL - original licence link has changed is not relivant.
11582  *
11583  * Fork - LGPL
11584  * <script type="text/javascript">
11585  */
11586  
11587 /**
11588  * Global Ajax request class.
11589  * 
11590  * @class Roo.Ajax
11591  * @extends Roo.data.Connection
11592  * @static
11593  * 
11594  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11595  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11596  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11597  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11598  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11599  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11600  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11601  */
11602 Roo.Ajax = new Roo.data.Connection({
11603     // fix up the docs
11604     /**
11605      * @scope Roo.Ajax
11606      * @type {Boolear} 
11607      */
11608     autoAbort : false,
11609
11610     /**
11611      * Serialize the passed form into a url encoded string
11612      * @scope Roo.Ajax
11613      * @param {String/HTMLElement} form
11614      * @return {String}
11615      */
11616     serializeForm : function(form){
11617         return Roo.lib.Ajax.serializeForm(form);
11618     }
11619 });/*
11620  * Based on:
11621  * Ext JS Library 1.1.1
11622  * Copyright(c) 2006-2007, Ext JS, LLC.
11623  *
11624  * Originally Released Under LGPL - original licence link has changed is not relivant.
11625  *
11626  * Fork - LGPL
11627  * <script type="text/javascript">
11628  */
11629
11630  
11631 /**
11632  * @class Roo.UpdateManager
11633  * @extends Roo.util.Observable
11634  * Provides AJAX-style update for Element object.<br><br>
11635  * Usage:<br>
11636  * <pre><code>
11637  * // Get it from a Roo.Element object
11638  * var el = Roo.get("foo");
11639  * var mgr = el.getUpdateManager();
11640  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11641  * ...
11642  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11643  * <br>
11644  * // or directly (returns the same UpdateManager instance)
11645  * var mgr = new Roo.UpdateManager("myElementId");
11646  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11647  * mgr.on("update", myFcnNeedsToKnow);
11648  * <br>
11649    // short handed call directly from the element object
11650    Roo.get("foo").load({
11651         url: "bar.php",
11652         scripts:true,
11653         params: "for=bar",
11654         text: "Loading Foo..."
11655    });
11656  * </code></pre>
11657  * @constructor
11658  * Create new UpdateManager directly.
11659  * @param {String/HTMLElement/Roo.Element} el The element to update
11660  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11661  */
11662 Roo.UpdateManager = function(el, forceNew){
11663     el = Roo.get(el);
11664     if(!forceNew && el.updateManager){
11665         return el.updateManager;
11666     }
11667     /**
11668      * The Element object
11669      * @type Roo.Element
11670      */
11671     this.el = el;
11672     /**
11673      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11674      * @type String
11675      */
11676     this.defaultUrl = null;
11677
11678     this.addEvents({
11679         /**
11680          * @event beforeupdate
11681          * Fired before an update is made, return false from your handler and the update is cancelled.
11682          * @param {Roo.Element} el
11683          * @param {String/Object/Function} url
11684          * @param {String/Object} params
11685          */
11686         "beforeupdate": true,
11687         /**
11688          * @event update
11689          * Fired after successful update is made.
11690          * @param {Roo.Element} el
11691          * @param {Object} oResponseObject The response Object
11692          */
11693         "update": true,
11694         /**
11695          * @event failure
11696          * Fired on update failure.
11697          * @param {Roo.Element} el
11698          * @param {Object} oResponseObject The response Object
11699          */
11700         "failure": true
11701     });
11702     var d = Roo.UpdateManager.defaults;
11703     /**
11704      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11705      * @type String
11706      */
11707     this.sslBlankUrl = d.sslBlankUrl;
11708     /**
11709      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11710      * @type Boolean
11711      */
11712     this.disableCaching = d.disableCaching;
11713     /**
11714      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11715      * @type String
11716      */
11717     this.indicatorText = d.indicatorText;
11718     /**
11719      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11720      * @type String
11721      */
11722     this.showLoadIndicator = d.showLoadIndicator;
11723     /**
11724      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11725      * @type Number
11726      */
11727     this.timeout = d.timeout;
11728
11729     /**
11730      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11731      * @type Boolean
11732      */
11733     this.loadScripts = d.loadScripts;
11734
11735     /**
11736      * Transaction object of current executing transaction
11737      */
11738     this.transaction = null;
11739
11740     /**
11741      * @private
11742      */
11743     this.autoRefreshProcId = null;
11744     /**
11745      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.refreshDelegate = this.refresh.createDelegate(this);
11749     /**
11750      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11751      * @type Function
11752      */
11753     this.updateDelegate = this.update.createDelegate(this);
11754     /**
11755      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11756      * @type Function
11757      */
11758     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11759     /**
11760      * @private
11761      */
11762     this.successDelegate = this.processSuccess.createDelegate(this);
11763     /**
11764      * @private
11765      */
11766     this.failureDelegate = this.processFailure.createDelegate(this);
11767
11768     if(!this.renderer){
11769      /**
11770       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11771       */
11772     this.renderer = new Roo.UpdateManager.BasicRenderer();
11773     }
11774     
11775     Roo.UpdateManager.superclass.constructor.call(this);
11776 };
11777
11778 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11779     /**
11780      * Get the Element this UpdateManager is bound to
11781      * @return {Roo.Element} The element
11782      */
11783     getEl : function(){
11784         return this.el;
11785     },
11786     /**
11787      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11788      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11789 <pre><code>
11790 um.update({<br/>
11791     url: "your-url.php",<br/>
11792     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11793     callback: yourFunction,<br/>
11794     scope: yourObject, //(optional scope)  <br/>
11795     discardUrl: false, <br/>
11796     nocache: false,<br/>
11797     text: "Loading...",<br/>
11798     timeout: 30,<br/>
11799     scripts: false<br/>
11800 });
11801 </code></pre>
11802      * The only required property is url. The optional properties nocache, text and scripts
11803      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11804      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11805      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11806      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11807      */
11808     update : function(url, params, callback, discardUrl){
11809         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11810             var method = this.method,
11811                 cfg;
11812             if(typeof url == "object"){ // must be config object
11813                 cfg = url;
11814                 url = cfg.url;
11815                 params = params || cfg.params;
11816                 callback = callback || cfg.callback;
11817                 discardUrl = discardUrl || cfg.discardUrl;
11818                 if(callback && cfg.scope){
11819                     callback = callback.createDelegate(cfg.scope);
11820                 }
11821                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11822                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11823                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11824                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11825                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11826             }
11827             this.showLoading();
11828             if(!discardUrl){
11829                 this.defaultUrl = url;
11830             }
11831             if(typeof url == "function"){
11832                 url = url.call(this);
11833             }
11834
11835             method = method || (params ? "POST" : "GET");
11836             if(method == "GET"){
11837                 url = this.prepareUrl(url);
11838             }
11839
11840             var o = Roo.apply(cfg ||{}, {
11841                 url : url,
11842                 params: params,
11843                 success: this.successDelegate,
11844                 failure: this.failureDelegate,
11845                 callback: undefined,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11848             });
11849             Roo.log("updated manager called with timeout of " + o.timeout);
11850             this.transaction = Roo.Ajax.request(o);
11851         }
11852     },
11853
11854     /**
11855      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11856      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11857      * @param {String/HTMLElement} form The form Id or form element
11858      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11859      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11860      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11861      */
11862     formUpdate : function(form, url, reset, callback){
11863         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11864             if(typeof url == "function"){
11865                 url = url.call(this);
11866             }
11867             form = Roo.getDom(form);
11868             this.transaction = Roo.Ajax.request({
11869                 form: form,
11870                 url:url,
11871                 success: this.successDelegate,
11872                 failure: this.failureDelegate,
11873                 timeout: (this.timeout*1000),
11874                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11875             });
11876             this.showLoading.defer(1, this);
11877         }
11878     },
11879
11880     /**
11881      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11882      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11883      */
11884     refresh : function(callback){
11885         if(this.defaultUrl == null){
11886             return;
11887         }
11888         this.update(this.defaultUrl, null, callback, true);
11889     },
11890
11891     /**
11892      * Set this element to auto refresh.
11893      * @param {Number} interval How often to update (in seconds).
11894      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11895      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11896      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11897      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11898      */
11899     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11900         if(refreshNow){
11901             this.update(url || this.defaultUrl, params, callback, true);
11902         }
11903         if(this.autoRefreshProcId){
11904             clearInterval(this.autoRefreshProcId);
11905         }
11906         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11907     },
11908
11909     /**
11910      * Stop auto refresh on this element.
11911      */
11912      stopAutoRefresh : function(){
11913         if(this.autoRefreshProcId){
11914             clearInterval(this.autoRefreshProcId);
11915             delete this.autoRefreshProcId;
11916         }
11917     },
11918
11919     isAutoRefreshing : function(){
11920        return this.autoRefreshProcId ? true : false;
11921     },
11922     /**
11923      * Called to update the element to "Loading" state. Override to perform custom action.
11924      */
11925     showLoading : function(){
11926         if(this.showLoadIndicator){
11927             this.el.update(this.indicatorText);
11928         }
11929     },
11930
11931     /**
11932      * Adds unique parameter to query string if disableCaching = true
11933      * @private
11934      */
11935     prepareUrl : function(url){
11936         if(this.disableCaching){
11937             var append = "_dc=" + (new Date().getTime());
11938             if(url.indexOf("?") !== -1){
11939                 url += "&" + append;
11940             }else{
11941                 url += "?" + append;
11942             }
11943         }
11944         return url;
11945     },
11946
11947     /**
11948      * @private
11949      */
11950     processSuccess : function(response){
11951         this.transaction = null;
11952         if(response.argument.form && response.argument.reset){
11953             try{ // put in try/catch since some older FF releases had problems with this
11954                 response.argument.form.reset();
11955             }catch(e){}
11956         }
11957         if(this.loadScripts){
11958             this.renderer.render(this.el, response, this,
11959                 this.updateComplete.createDelegate(this, [response]));
11960         }else{
11961             this.renderer.render(this.el, response, this);
11962             this.updateComplete(response);
11963         }
11964     },
11965
11966     updateComplete : function(response){
11967         this.fireEvent("update", this.el, response);
11968         if(typeof response.argument.callback == "function"){
11969             response.argument.callback(this.el, true, response);
11970         }
11971     },
11972
11973     /**
11974      * @private
11975      */
11976     processFailure : function(response){
11977         this.transaction = null;
11978         this.fireEvent("failure", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, false, response);
11981         }
11982     },
11983
11984     /**
11985      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11986      * @param {Object} renderer The object implementing the render() method
11987      */
11988     setRenderer : function(renderer){
11989         this.renderer = renderer;
11990     },
11991
11992     getRenderer : function(){
11993        return this.renderer;
11994     },
11995
11996     /**
11997      * Set the defaultUrl used for updates
11998      * @param {String/Function} defaultUrl The url or a function to call to get the url
11999      */
12000     setDefaultUrl : function(defaultUrl){
12001         this.defaultUrl = defaultUrl;
12002     },
12003
12004     /**
12005      * Aborts the executing transaction
12006      */
12007     abort : function(){
12008         if(this.transaction){
12009             Roo.Ajax.abort(this.transaction);
12010         }
12011     },
12012
12013     /**
12014      * Returns true if an update is in progress
12015      * @return {Boolean}
12016      */
12017     isUpdating : function(){
12018         if(this.transaction){
12019             return Roo.Ajax.isLoading(this.transaction);
12020         }
12021         return false;
12022     }
12023 });
12024
12025 /**
12026  * @class Roo.UpdateManager.defaults
12027  * @static (not really - but it helps the doc tool)
12028  * The defaults collection enables customizing the default properties of UpdateManager
12029  */
12030    Roo.UpdateManager.defaults = {
12031        /**
12032          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12033          * @type Number
12034          */
12035          timeout : 30,
12036
12037          /**
12038          * True to process scripts by default (Defaults to false).
12039          * @type Boolean
12040          */
12041         loadScripts : false,
12042
12043         /**
12044         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12045         * @type String
12046         */
12047         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12048         /**
12049          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12050          * @type Boolean
12051          */
12052         disableCaching : false,
12053         /**
12054          * Whether to show indicatorText when loading (Defaults to true).
12055          * @type Boolean
12056          */
12057         showLoadIndicator : true,
12058         /**
12059          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12060          * @type String
12061          */
12062         indicatorText : '<div class="loading-indicator">Loading...</div>'
12063    };
12064
12065 /**
12066  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12067  *Usage:
12068  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12069  * @param {String/HTMLElement/Roo.Element} el The element to update
12070  * @param {String} url The url
12071  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12072  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12073  * @static
12074  * @deprecated
12075  * @member Roo.UpdateManager
12076  */
12077 Roo.UpdateManager.updateElement = function(el, url, params, options){
12078     var um = Roo.get(el, true).getUpdateManager();
12079     Roo.apply(um, options);
12080     um.update(url, params, options ? options.callback : null);
12081 };
12082 // alias for backwards compat
12083 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12084 /**
12085  * @class Roo.UpdateManager.BasicRenderer
12086  * Default Content renderer. Updates the elements innerHTML with the responseText.
12087  */
12088 Roo.UpdateManager.BasicRenderer = function(){};
12089
12090 Roo.UpdateManager.BasicRenderer.prototype = {
12091     /**
12092      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12093      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12094      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12095      * @param {Roo.Element} el The element being rendered
12096      * @param {Object} response The YUI Connect response object
12097      * @param {UpdateManager} updateManager The calling update manager
12098      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12099      */
12100      render : function(el, response, updateManager, callback){
12101         el.update(response.responseText, updateManager.loadScripts, callback);
12102     }
12103 };
12104 /*
12105  * Based on:
12106  * Roo JS
12107  * (c)) Alan Knowles
12108  * Licence : LGPL
12109  */
12110
12111
12112 /**
12113  * @class Roo.DomTemplate
12114  * @extends Roo.Template
12115  * An effort at a dom based template engine..
12116  *
12117  * Similar to XTemplate, except it uses dom parsing to create the template..
12118  *
12119  * Supported features:
12120  *
12121  *  Tags:
12122
12123 <pre><code>
12124       {a_variable} - output encoded.
12125       {a_variable.format:("Y-m-d")} - call a method on the variable
12126       {a_variable:raw} - unencoded output
12127       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12128       {a_variable:this.method_on_template(...)} - call a method on the template object.
12129  
12130 </code></pre>
12131  *  The tpl tag:
12132 <pre><code>
12133         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12134         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12135         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12136         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12137   
12138 </code></pre>
12139  *      
12140  */
12141 Roo.DomTemplate = function()
12142 {
12143      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12144      if (this.html) {
12145         this.compile();
12146      }
12147 };
12148
12149
12150 Roo.extend(Roo.DomTemplate, Roo.Template, {
12151     /**
12152      * id counter for sub templates.
12153      */
12154     id : 0,
12155     /**
12156      * flag to indicate if dom parser is inside a pre,
12157      * it will strip whitespace if not.
12158      */
12159     inPre : false,
12160     
12161     /**
12162      * The various sub templates
12163      */
12164     tpls : false,
12165     
12166     
12167     
12168     /**
12169      *
12170      * basic tag replacing syntax
12171      * WORD:WORD()
12172      *
12173      * // you can fake an object call by doing this
12174      *  x.t:(test,tesT) 
12175      * 
12176      */
12177     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12178     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12179     
12180     iterChild : function (node, method) {
12181         
12182         var oldPre = this.inPre;
12183         if (node.tagName == 'PRE') {
12184             this.inPre = true;
12185         }
12186         for( var i = 0; i < node.childNodes.length; i++) {
12187             method.call(this, node.childNodes[i]);
12188         }
12189         this.inPre = oldPre;
12190     },
12191     
12192     
12193     
12194     /**
12195      * compile the template
12196      *
12197      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12198      *
12199      */
12200     compile: function()
12201     {
12202         var s = this.html;
12203         
12204         // covert the html into DOM...
12205         var doc = false;
12206         var div =false;
12207         try {
12208             doc = document.implementation.createHTMLDocument("");
12209             doc.documentElement.innerHTML =   this.html  ;
12210             div = doc.documentElement;
12211         } catch (e) {
12212             // old IE... - nasty -- it causes all sorts of issues.. with
12213             // images getting pulled from server..
12214             div = document.createElement('div');
12215             div.innerHTML = this.html;
12216         }
12217         //doc.documentElement.innerHTML = htmlBody
12218          
12219         
12220         
12221         this.tpls = [];
12222         var _t = this;
12223         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12224         
12225         var tpls = this.tpls;
12226         
12227         // create a top level template from the snippet..
12228         
12229         //Roo.log(div.innerHTML);
12230         
12231         var tpl = {
12232             uid : 'master',
12233             id : this.id++,
12234             attr : false,
12235             value : false,
12236             body : div.innerHTML,
12237             
12238             forCall : false,
12239             execCall : false,
12240             dom : div,
12241             isTop : true
12242             
12243         };
12244         tpls.unshift(tpl);
12245         
12246         
12247         // compile them...
12248         this.tpls = [];
12249         Roo.each(tpls, function(tp){
12250             this.compileTpl(tp);
12251             this.tpls[tp.id] = tp;
12252         }, this);
12253         
12254         this.master = tpls[0];
12255         return this;
12256         
12257         
12258     },
12259     
12260     compileNode : function(node, istop) {
12261         // test for
12262         //Roo.log(node);
12263         
12264         
12265         // skip anything not a tag..
12266         if (node.nodeType != 1) {
12267             if (node.nodeType == 3 && !this.inPre) {
12268                 // reduce white space..
12269                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12270                 
12271             }
12272             return;
12273         }
12274         
12275         var tpl = {
12276             uid : false,
12277             id : false,
12278             attr : false,
12279             value : false,
12280             body : '',
12281             
12282             forCall : false,
12283             execCall : false,
12284             dom : false,
12285             isTop : istop
12286             
12287             
12288         };
12289         
12290         
12291         switch(true) {
12292             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12293             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12294             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12295             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12296             // no default..
12297         }
12298         
12299         
12300         if (!tpl.attr) {
12301             // just itterate children..
12302             this.iterChild(node,this.compileNode);
12303             return;
12304         }
12305         tpl.uid = this.id++;
12306         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12307         node.removeAttribute('roo-'+ tpl.attr);
12308         if (tpl.attr != 'name') {
12309             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12310             node.parentNode.replaceChild(placeholder,  node);
12311         } else {
12312             
12313             var placeholder =  document.createElement('span');
12314             placeholder.className = 'roo-tpl-' + tpl.value;
12315             node.parentNode.replaceChild(placeholder,  node);
12316         }
12317         
12318         // parent now sees '{domtplXXXX}
12319         this.iterChild(node,this.compileNode);
12320         
12321         // we should now have node body...
12322         var div = document.createElement('div');
12323         div.appendChild(node);
12324         tpl.dom = node;
12325         // this has the unfortunate side effect of converting tagged attributes
12326         // eg. href="{...}" into %7C...%7D
12327         // this has been fixed by searching for those combo's although it's a bit hacky..
12328         
12329         
12330         tpl.body = div.innerHTML;
12331         
12332         
12333          
12334         tpl.id = tpl.uid;
12335         switch(tpl.attr) {
12336             case 'for' :
12337                 switch (tpl.value) {
12338                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12339                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12340                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12341                 }
12342                 break;
12343             
12344             case 'exec':
12345                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12346                 break;
12347             
12348             case 'if':     
12349                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12350                 break;
12351             
12352             case 'name':
12353                 tpl.id  = tpl.value; // replace non characters???
12354                 break;
12355             
12356         }
12357         
12358         
12359         this.tpls.push(tpl);
12360         
12361         
12362         
12363     },
12364     
12365     
12366     
12367     
12368     /**
12369      * Compile a segment of the template into a 'sub-template'
12370      *
12371      * 
12372      * 
12373      *
12374      */
12375     compileTpl : function(tpl)
12376     {
12377         var fm = Roo.util.Format;
12378         var useF = this.disableFormats !== true;
12379         
12380         var sep = Roo.isGecko ? "+\n" : ",\n";
12381         
12382         var undef = function(str) {
12383             Roo.debug && Roo.log("Property not found :"  + str);
12384             return '';
12385         };
12386           
12387         //Roo.log(tpl.body);
12388         
12389         
12390         
12391         var fn = function(m, lbrace, name, format, args)
12392         {
12393             //Roo.log("ARGS");
12394             //Roo.log(arguments);
12395             args = args ? args.replace(/\\'/g,"'") : args;
12396             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12397             if (typeof(format) == 'undefined') {
12398                 format =  'htmlEncode'; 
12399             }
12400             if (format == 'raw' ) {
12401                 format = false;
12402             }
12403             
12404             if(name.substr(0, 6) == 'domtpl'){
12405                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12406             }
12407             
12408             // build an array of options to determine if value is undefined..
12409             
12410             // basically get 'xxxx.yyyy' then do
12411             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12412             //    (function () { Roo.log("Property not found"); return ''; })() :
12413             //    ......
12414             
12415             var udef_ar = [];
12416             var lookfor = '';
12417             Roo.each(name.split('.'), function(st) {
12418                 lookfor += (lookfor.length ? '.': '') + st;
12419                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12420             });
12421             
12422             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12423             
12424             
12425             if(format && useF){
12426                 
12427                 args = args ? ',' + args : "";
12428                  
12429                 if(format.substr(0, 5) != "this."){
12430                     format = "fm." + format + '(';
12431                 }else{
12432                     format = 'this.call("'+ format.substr(5) + '", ';
12433                     args = ", values";
12434                 }
12435                 
12436                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12437             }
12438              
12439             if (args && args.length) {
12440                 // called with xxyx.yuu:(test,test)
12441                 // change to ()
12442                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12443             }
12444             // raw.. - :raw modifier..
12445             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12446             
12447         };
12448         var body;
12449         // branched to use + in gecko and [].join() in others
12450         if(Roo.isGecko){
12451             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12452                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12453                     "';};};";
12454         }else{
12455             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12456             body.push(tpl.body.replace(/(\r\n|\n)/g,
12457                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12458             body.push("'].join('');};};");
12459             body = body.join('');
12460         }
12461         
12462         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12463        
12464         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12465         eval(body);
12466         
12467         return this;
12468     },
12469      
12470     /**
12471      * same as applyTemplate, except it's done to one of the subTemplates
12472      * when using named templates, you can do:
12473      *
12474      * var str = pl.applySubTemplate('your-name', values);
12475      *
12476      * 
12477      * @param {Number} id of the template
12478      * @param {Object} values to apply to template
12479      * @param {Object} parent (normaly the instance of this object)
12480      */
12481     applySubTemplate : function(id, values, parent)
12482     {
12483         
12484         
12485         var t = this.tpls[id];
12486         
12487         
12488         try { 
12489             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12490                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12491                 return '';
12492             }
12493         } catch(e) {
12494             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12495             Roo.log(values);
12496           
12497             return '';
12498         }
12499         try { 
12500             
12501             if(t.execCall && t.execCall.call(this, values, parent)){
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507             return '';
12508         }
12509         
12510         try {
12511             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12512             parent = t.target ? values : parent;
12513             if(t.forCall && vs instanceof Array){
12514                 var buf = [];
12515                 for(var i = 0, len = vs.length; i < len; i++){
12516                     try {
12517                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12518                     } catch (e) {
12519                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12520                         Roo.log(e.body);
12521                         //Roo.log(t.compiled);
12522                         Roo.log(vs[i]);
12523                     }   
12524                 }
12525                 return buf.join('');
12526             }
12527         } catch (e) {
12528             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12529             Roo.log(values);
12530             return '';
12531         }
12532         try {
12533             return t.compiled.call(this, vs, parent);
12534         } catch (e) {
12535             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12536             Roo.log(e.body);
12537             //Roo.log(t.compiled);
12538             Roo.log(values);
12539             return '';
12540         }
12541     },
12542
12543    
12544
12545     applyTemplate : function(values){
12546         return this.master.compiled.call(this, values, {});
12547         //var s = this.subs;
12548     },
12549
12550     apply : function(){
12551         return this.applyTemplate.apply(this, arguments);
12552     }
12553
12554  });
12555
12556 Roo.DomTemplate.from = function(el){
12557     el = Roo.getDom(el);
12558     return new Roo.Domtemplate(el.value || el.innerHTML);
12559 };/*
12560  * Based on:
12561  * Ext JS Library 1.1.1
12562  * Copyright(c) 2006-2007, Ext JS, LLC.
12563  *
12564  * Originally Released Under LGPL - original licence link has changed is not relivant.
12565  *
12566  * Fork - LGPL
12567  * <script type="text/javascript">
12568  */
12569
12570 /**
12571  * @class Roo.util.DelayedTask
12572  * Provides a convenient method of performing setTimeout where a new
12573  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12574  * You can use this class to buffer
12575  * the keypress events for a certain number of milliseconds, and perform only if they stop
12576  * for that amount of time.
12577  * @constructor The parameters to this constructor serve as defaults and are not required.
12578  * @param {Function} fn (optional) The default function to timeout
12579  * @param {Object} scope (optional) The default scope of that timeout
12580  * @param {Array} args (optional) The default Array of arguments
12581  */
12582 Roo.util.DelayedTask = function(fn, scope, args){
12583     var id = null, d, t;
12584
12585     var call = function(){
12586         var now = new Date().getTime();
12587         if(now - t >= d){
12588             clearInterval(id);
12589             id = null;
12590             fn.apply(scope, args || []);
12591         }
12592     };
12593     /**
12594      * Cancels any pending timeout and queues a new one
12595      * @param {Number} delay The milliseconds to delay
12596      * @param {Function} newFn (optional) Overrides function passed to constructor
12597      * @param {Object} newScope (optional) Overrides scope passed to constructor
12598      * @param {Array} newArgs (optional) Overrides args passed to constructor
12599      */
12600     this.delay = function(delay, newFn, newScope, newArgs){
12601         if(id && delay != d){
12602             this.cancel();
12603         }
12604         d = delay;
12605         t = new Date().getTime();
12606         fn = newFn || fn;
12607         scope = newScope || scope;
12608         args = newArgs || args;
12609         if(!id){
12610             id = setInterval(call, d);
12611         }
12612     };
12613
12614     /**
12615      * Cancel the last queued timeout
12616      */
12617     this.cancel = function(){
12618         if(id){
12619             clearInterval(id);
12620             id = null;
12621         }
12622     };
12623 };/*
12624  * Based on:
12625  * Ext JS Library 1.1.1
12626  * Copyright(c) 2006-2007, Ext JS, LLC.
12627  *
12628  * Originally Released Under LGPL - original licence link has changed is not relivant.
12629  *
12630  * Fork - LGPL
12631  * <script type="text/javascript">
12632  */
12633  
12634  
12635 Roo.util.TaskRunner = function(interval){
12636     interval = interval || 10;
12637     var tasks = [], removeQueue = [];
12638     var id = 0;
12639     var running = false;
12640
12641     var stopThread = function(){
12642         running = false;
12643         clearInterval(id);
12644         id = 0;
12645     };
12646
12647     var startThread = function(){
12648         if(!running){
12649             running = true;
12650             id = setInterval(runTasks, interval);
12651         }
12652     };
12653
12654     var removeTask = function(task){
12655         removeQueue.push(task);
12656         if(task.onStop){
12657             task.onStop();
12658         }
12659     };
12660
12661     var runTasks = function(){
12662         if(removeQueue.length > 0){
12663             for(var i = 0, len = removeQueue.length; i < len; i++){
12664                 tasks.remove(removeQueue[i]);
12665             }
12666             removeQueue = [];
12667             if(tasks.length < 1){
12668                 stopThread();
12669                 return;
12670             }
12671         }
12672         var now = new Date().getTime();
12673         for(var i = 0, len = tasks.length; i < len; ++i){
12674             var t = tasks[i];
12675             var itime = now - t.taskRunTime;
12676             if(t.interval <= itime){
12677                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12678                 t.taskRunTime = now;
12679                 if(rt === false || t.taskRunCount === t.repeat){
12680                     removeTask(t);
12681                     return;
12682                 }
12683             }
12684             if(t.duration && t.duration <= (now - t.taskStartTime)){
12685                 removeTask(t);
12686             }
12687         }
12688     };
12689
12690     /**
12691      * Queues a new task.
12692      * @param {Object} task
12693      */
12694     this.start = function(task){
12695         tasks.push(task);
12696         task.taskStartTime = new Date().getTime();
12697         task.taskRunTime = 0;
12698         task.taskRunCount = 0;
12699         startThread();
12700         return task;
12701     };
12702
12703     this.stop = function(task){
12704         removeTask(task);
12705         return task;
12706     };
12707
12708     this.stopAll = function(){
12709         stopThread();
12710         for(var i = 0, len = tasks.length; i < len; i++){
12711             if(tasks[i].onStop){
12712                 tasks[i].onStop();
12713             }
12714         }
12715         tasks = [];
12716         removeQueue = [];
12717     };
12718 };
12719
12720 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12721  * Based on:
12722  * Ext JS Library 1.1.1
12723  * Copyright(c) 2006-2007, Ext JS, LLC.
12724  *
12725  * Originally Released Under LGPL - original licence link has changed is not relivant.
12726  *
12727  * Fork - LGPL
12728  * <script type="text/javascript">
12729  */
12730
12731  
12732 /**
12733  * @class Roo.util.MixedCollection
12734  * @extends Roo.util.Observable
12735  * A Collection class that maintains both numeric indexes and keys and exposes events.
12736  * @constructor
12737  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12738  * collection (defaults to false)
12739  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12740  * and return the key value for that item.  This is used when available to look up the key on items that
12741  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12742  * equivalent to providing an implementation for the {@link #getKey} method.
12743  */
12744 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12745     this.items = [];
12746     this.map = {};
12747     this.keys = [];
12748     this.length = 0;
12749     this.addEvents({
12750         /**
12751          * @event clear
12752          * Fires when the collection is cleared.
12753          */
12754         "clear" : true,
12755         /**
12756          * @event add
12757          * Fires when an item is added to the collection.
12758          * @param {Number} index The index at which the item was added.
12759          * @param {Object} o The item added.
12760          * @param {String} key The key associated with the added item.
12761          */
12762         "add" : true,
12763         /**
12764          * @event replace
12765          * Fires when an item is replaced in the collection.
12766          * @param {String} key he key associated with the new added.
12767          * @param {Object} old The item being replaced.
12768          * @param {Object} new The new item.
12769          */
12770         "replace" : true,
12771         /**
12772          * @event remove
12773          * Fires when an item is removed from the collection.
12774          * @param {Object} o The item being removed.
12775          * @param {String} key (optional) The key associated with the removed item.
12776          */
12777         "remove" : true,
12778         "sort" : true
12779     });
12780     this.allowFunctions = allowFunctions === true;
12781     if(keyFn){
12782         this.getKey = keyFn;
12783     }
12784     Roo.util.MixedCollection.superclass.constructor.call(this);
12785 };
12786
12787 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12788     allowFunctions : false,
12789     
12790 /**
12791  * Adds an item to the collection.
12792  * @param {String} key The key to associate with the item
12793  * @param {Object} o The item to add.
12794  * @return {Object} The item added.
12795  */
12796     add : function(key, o){
12797         if(arguments.length == 1){
12798             o = arguments[0];
12799             key = this.getKey(o);
12800         }
12801         if(typeof key == "undefined" || key === null){
12802             this.length++;
12803             this.items.push(o);
12804             this.keys.push(null);
12805         }else{
12806             var old = this.map[key];
12807             if(old){
12808                 return this.replace(key, o);
12809             }
12810             this.length++;
12811             this.items.push(o);
12812             this.map[key] = o;
12813             this.keys.push(key);
12814         }
12815         this.fireEvent("add", this.length-1, o, key);
12816         return o;
12817     },
12818        
12819 /**
12820   * MixedCollection has a generic way to fetch keys if you implement getKey.
12821 <pre><code>
12822 // normal way
12823 var mc = new Roo.util.MixedCollection();
12824 mc.add(someEl.dom.id, someEl);
12825 mc.add(otherEl.dom.id, otherEl);
12826 //and so on
12827
12828 // using getKey
12829 var mc = new Roo.util.MixedCollection();
12830 mc.getKey = function(el){
12831    return el.dom.id;
12832 };
12833 mc.add(someEl);
12834 mc.add(otherEl);
12835
12836 // or via the constructor
12837 var mc = new Roo.util.MixedCollection(false, function(el){
12838    return el.dom.id;
12839 });
12840 mc.add(someEl);
12841 mc.add(otherEl);
12842 </code></pre>
12843  * @param o {Object} The item for which to find the key.
12844  * @return {Object} The key for the passed item.
12845  */
12846     getKey : function(o){
12847          return o.id; 
12848     },
12849    
12850 /**
12851  * Replaces an item in the collection.
12852  * @param {String} key The key associated with the item to replace, or the item to replace.
12853  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12854  * @return {Object}  The new item.
12855  */
12856     replace : function(key, o){
12857         if(arguments.length == 1){
12858             o = arguments[0];
12859             key = this.getKey(o);
12860         }
12861         var old = this.item(key);
12862         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12863              return this.add(key, o);
12864         }
12865         var index = this.indexOfKey(key);
12866         this.items[index] = o;
12867         this.map[key] = o;
12868         this.fireEvent("replace", key, old, o);
12869         return o;
12870     },
12871    
12872 /**
12873  * Adds all elements of an Array or an Object to the collection.
12874  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12875  * an Array of values, each of which are added to the collection.
12876  */
12877     addAll : function(objs){
12878         if(arguments.length > 1 || objs instanceof Array){
12879             var args = arguments.length > 1 ? arguments : objs;
12880             for(var i = 0, len = args.length; i < len; i++){
12881                 this.add(args[i]);
12882             }
12883         }else{
12884             for(var key in objs){
12885                 if(this.allowFunctions || typeof objs[key] != "function"){
12886                     this.add(key, objs[key]);
12887                 }
12888             }
12889         }
12890     },
12891    
12892 /**
12893  * Executes the specified function once for every item in the collection, passing each
12894  * item as the first and only parameter. returning false from the function will stop the iteration.
12895  * @param {Function} fn The function to execute for each item.
12896  * @param {Object} scope (optional) The scope in which to execute the function.
12897  */
12898     each : function(fn, scope){
12899         var items = [].concat(this.items); // each safe for removal
12900         for(var i = 0, len = items.length; i < len; i++){
12901             if(fn.call(scope || items[i], items[i], i, len) === false){
12902                 break;
12903             }
12904         }
12905     },
12906    
12907 /**
12908  * Executes the specified function once for every key in the collection, passing each
12909  * key, and its associated item as the first two parameters.
12910  * @param {Function} fn The function to execute for each item.
12911  * @param {Object} scope (optional) The scope in which to execute the function.
12912  */
12913     eachKey : function(fn, scope){
12914         for(var i = 0, len = this.keys.length; i < len; i++){
12915             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12916         }
12917     },
12918    
12919 /**
12920  * Returns the first item in the collection which elicits a true return value from the
12921  * passed selection function.
12922  * @param {Function} fn The selection function to execute for each item.
12923  * @param {Object} scope (optional) The scope in which to execute the function.
12924  * @return {Object} The first item in the collection which returned true from the selection function.
12925  */
12926     find : function(fn, scope){
12927         for(var i = 0, len = this.items.length; i < len; i++){
12928             if(fn.call(scope || window, this.items[i], this.keys[i])){
12929                 return this.items[i];
12930             }
12931         }
12932         return null;
12933     },
12934    
12935 /**
12936  * Inserts an item at the specified index in the collection.
12937  * @param {Number} index The index to insert the item at.
12938  * @param {String} key The key to associate with the new item, or the item itself.
12939  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12940  * @return {Object} The item inserted.
12941  */
12942     insert : function(index, key, o){
12943         if(arguments.length == 2){
12944             o = arguments[1];
12945             key = this.getKey(o);
12946         }
12947         if(index >= this.length){
12948             return this.add(key, o);
12949         }
12950         this.length++;
12951         this.items.splice(index, 0, o);
12952         if(typeof key != "undefined" && key != null){
12953             this.map[key] = o;
12954         }
12955         this.keys.splice(index, 0, key);
12956         this.fireEvent("add", index, o, key);
12957         return o;
12958     },
12959    
12960 /**
12961  * Removed an item from the collection.
12962  * @param {Object} o The item to remove.
12963  * @return {Object} The item removed.
12964  */
12965     remove : function(o){
12966         return this.removeAt(this.indexOf(o));
12967     },
12968    
12969 /**
12970  * Remove an item from a specified index in the collection.
12971  * @param {Number} index The index within the collection of the item to remove.
12972  */
12973     removeAt : function(index){
12974         if(index < this.length && index >= 0){
12975             this.length--;
12976             var o = this.items[index];
12977             this.items.splice(index, 1);
12978             var key = this.keys[index];
12979             if(typeof key != "undefined"){
12980                 delete this.map[key];
12981             }
12982             this.keys.splice(index, 1);
12983             this.fireEvent("remove", o, key);
12984         }
12985     },
12986    
12987 /**
12988  * Removed an item associated with the passed key fom the collection.
12989  * @param {String} key The key of the item to remove.
12990  */
12991     removeKey : function(key){
12992         return this.removeAt(this.indexOfKey(key));
12993     },
12994    
12995 /**
12996  * Returns the number of items in the collection.
12997  * @return {Number} the number of items in the collection.
12998  */
12999     getCount : function(){
13000         return this.length; 
13001     },
13002    
13003 /**
13004  * Returns index within the collection of the passed Object.
13005  * @param {Object} o The item to find the index of.
13006  * @return {Number} index of the item.
13007  */
13008     indexOf : function(o){
13009         if(!this.items.indexOf){
13010             for(var i = 0, len = this.items.length; i < len; i++){
13011                 if(this.items[i] == o) return i;
13012             }
13013             return -1;
13014         }else{
13015             return this.items.indexOf(o);
13016         }
13017     },
13018    
13019 /**
13020  * Returns index within the collection of the passed key.
13021  * @param {String} key The key to find the index of.
13022  * @return {Number} index of the key.
13023  */
13024     indexOfKey : function(key){
13025         if(!this.keys.indexOf){
13026             for(var i = 0, len = this.keys.length; i < len; i++){
13027                 if(this.keys[i] == key) return i;
13028             }
13029             return -1;
13030         }else{
13031             return this.keys.indexOf(key);
13032         }
13033     },
13034    
13035 /**
13036  * Returns the item associated with the passed key OR index. Key has priority over index.
13037  * @param {String/Number} key The key or index of the item.
13038  * @return {Object} The item associated with the passed key.
13039  */
13040     item : function(key){
13041         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13042         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13043     },
13044     
13045 /**
13046  * Returns the item at the specified index.
13047  * @param {Number} index The index of the item.
13048  * @return {Object}
13049  */
13050     itemAt : function(index){
13051         return this.items[index];
13052     },
13053     
13054 /**
13055  * Returns the item associated with the passed key.
13056  * @param {String/Number} key The key of the item.
13057  * @return {Object} The item associated with the passed key.
13058  */
13059     key : function(key){
13060         return this.map[key];
13061     },
13062    
13063 /**
13064  * Returns true if the collection contains the passed Object as an item.
13065  * @param {Object} o  The Object to look for in the collection.
13066  * @return {Boolean} True if the collection contains the Object as an item.
13067  */
13068     contains : function(o){
13069         return this.indexOf(o) != -1;
13070     },
13071    
13072 /**
13073  * Returns true if the collection contains the passed Object as a key.
13074  * @param {String} key The key to look for in the collection.
13075  * @return {Boolean} True if the collection contains the Object as a key.
13076  */
13077     containsKey : function(key){
13078         return typeof this.map[key] != "undefined";
13079     },
13080    
13081 /**
13082  * Removes all items from the collection.
13083  */
13084     clear : function(){
13085         this.length = 0;
13086         this.items = [];
13087         this.keys = [];
13088         this.map = {};
13089         this.fireEvent("clear");
13090     },
13091    
13092 /**
13093  * Returns the first item in the collection.
13094  * @return {Object} the first item in the collection..
13095  */
13096     first : function(){
13097         return this.items[0]; 
13098     },
13099    
13100 /**
13101  * Returns the last item in the collection.
13102  * @return {Object} the last item in the collection..
13103  */
13104     last : function(){
13105         return this.items[this.length-1];   
13106     },
13107     
13108     _sort : function(property, dir, fn){
13109         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13110         fn = fn || function(a, b){
13111             return a-b;
13112         };
13113         var c = [], k = this.keys, items = this.items;
13114         for(var i = 0, len = items.length; i < len; i++){
13115             c[c.length] = {key: k[i], value: items[i], index: i};
13116         }
13117         c.sort(function(a, b){
13118             var v = fn(a[property], b[property]) * dsc;
13119             if(v == 0){
13120                 v = (a.index < b.index ? -1 : 1);
13121             }
13122             return v;
13123         });
13124         for(var i = 0, len = c.length; i < len; i++){
13125             items[i] = c[i].value;
13126             k[i] = c[i].key;
13127         }
13128         this.fireEvent("sort", this);
13129     },
13130     
13131     /**
13132      * Sorts this collection with the passed comparison function
13133      * @param {String} direction (optional) "ASC" or "DESC"
13134      * @param {Function} fn (optional) comparison function
13135      */
13136     sort : function(dir, fn){
13137         this._sort("value", dir, fn);
13138     },
13139     
13140     /**
13141      * Sorts this collection by keys
13142      * @param {String} direction (optional) "ASC" or "DESC"
13143      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13144      */
13145     keySort : function(dir, fn){
13146         this._sort("key", dir, fn || function(a, b){
13147             return String(a).toUpperCase()-String(b).toUpperCase();
13148         });
13149     },
13150     
13151     /**
13152      * Returns a range of items in this collection
13153      * @param {Number} startIndex (optional) defaults to 0
13154      * @param {Number} endIndex (optional) default to the last item
13155      * @return {Array} An array of items
13156      */
13157     getRange : function(start, end){
13158         var items = this.items;
13159         if(items.length < 1){
13160             return [];
13161         }
13162         start = start || 0;
13163         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13164         var r = [];
13165         if(start <= end){
13166             for(var i = start; i <= end; i++) {
13167                     r[r.length] = items[i];
13168             }
13169         }else{
13170             for(var i = start; i >= end; i--) {
13171                     r[r.length] = items[i];
13172             }
13173         }
13174         return r;
13175     },
13176         
13177     /**
13178      * Filter the <i>objects</i> in this collection by a specific property. 
13179      * Returns a new collection that has been filtered.
13180      * @param {String} property A property on your objects
13181      * @param {String/RegExp} value Either string that the property values 
13182      * should start with or a RegExp to test against the property
13183      * @return {MixedCollection} The new filtered collection
13184      */
13185     filter : function(property, value){
13186         if(!value.exec){ // not a regex
13187             value = String(value);
13188             if(value.length == 0){
13189                 return this.clone();
13190             }
13191             value = new RegExp("^" + Roo.escapeRe(value), "i");
13192         }
13193         return this.filterBy(function(o){
13194             return o && value.test(o[property]);
13195         });
13196         },
13197     
13198     /**
13199      * Filter by a function. * Returns a new collection that has been filtered.
13200      * The passed function will be called with each 
13201      * object in the collection. If the function returns true, the value is included 
13202      * otherwise it is filtered.
13203      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13204      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13205      * @return {MixedCollection} The new filtered collection
13206      */
13207     filterBy : function(fn, scope){
13208         var r = new Roo.util.MixedCollection();
13209         r.getKey = this.getKey;
13210         var k = this.keys, it = this.items;
13211         for(var i = 0, len = it.length; i < len; i++){
13212             if(fn.call(scope||this, it[i], k[i])){
13213                                 r.add(k[i], it[i]);
13214                         }
13215         }
13216         return r;
13217     },
13218     
13219     /**
13220      * Creates a duplicate of this collection
13221      * @return {MixedCollection}
13222      */
13223     clone : function(){
13224         var r = new Roo.util.MixedCollection();
13225         var k = this.keys, it = this.items;
13226         for(var i = 0, len = it.length; i < len; i++){
13227             r.add(k[i], it[i]);
13228         }
13229         r.getKey = this.getKey;
13230         return r;
13231     }
13232 });
13233 /**
13234  * Returns the item associated with the passed key or index.
13235  * @method
13236  * @param {String/Number} key The key or index of the item.
13237  * @return {Object} The item associated with the passed key.
13238  */
13239 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13240  * Based on:
13241  * Ext JS Library 1.1.1
13242  * Copyright(c) 2006-2007, Ext JS, LLC.
13243  *
13244  * Originally Released Under LGPL - original licence link has changed is not relivant.
13245  *
13246  * Fork - LGPL
13247  * <script type="text/javascript">
13248  */
13249 /**
13250  * @class Roo.util.JSON
13251  * Modified version of Douglas Crockford"s json.js that doesn"t
13252  * mess with the Object prototype 
13253  * http://www.json.org/js.html
13254  * @singleton
13255  */
13256 Roo.util.JSON = new (function(){
13257     var useHasOwn = {}.hasOwnProperty ? true : false;
13258     
13259     // crashes Safari in some instances
13260     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13261     
13262     var pad = function(n) {
13263         return n < 10 ? "0" + n : n;
13264     };
13265     
13266     var m = {
13267         "\b": '\\b',
13268         "\t": '\\t',
13269         "\n": '\\n',
13270         "\f": '\\f',
13271         "\r": '\\r',
13272         '"' : '\\"',
13273         "\\": '\\\\'
13274     };
13275
13276     var encodeString = function(s){
13277         if (/["\\\x00-\x1f]/.test(s)) {
13278             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13279                 var c = m[b];
13280                 if(c){
13281                     return c;
13282                 }
13283                 c = b.charCodeAt();
13284                 return "\\u00" +
13285                     Math.floor(c / 16).toString(16) +
13286                     (c % 16).toString(16);
13287             }) + '"';
13288         }
13289         return '"' + s + '"';
13290     };
13291     
13292     var encodeArray = function(o){
13293         var a = ["["], b, i, l = o.length, v;
13294             for (i = 0; i < l; i += 1) {
13295                 v = o[i];
13296                 switch (typeof v) {
13297                     case "undefined":
13298                     case "function":
13299                     case "unknown":
13300                         break;
13301                     default:
13302                         if (b) {
13303                             a.push(',');
13304                         }
13305                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13306                         b = true;
13307                 }
13308             }
13309             a.push("]");
13310             return a.join("");
13311     };
13312     
13313     var encodeDate = function(o){
13314         return '"' + o.getFullYear() + "-" +
13315                 pad(o.getMonth() + 1) + "-" +
13316                 pad(o.getDate()) + "T" +
13317                 pad(o.getHours()) + ":" +
13318                 pad(o.getMinutes()) + ":" +
13319                 pad(o.getSeconds()) + '"';
13320     };
13321     
13322     /**
13323      * Encodes an Object, Array or other value
13324      * @param {Mixed} o The variable to encode
13325      * @return {String} The JSON string
13326      */
13327     this.encode = function(o)
13328     {
13329         // should this be extended to fully wrap stringify..
13330         
13331         if(typeof o == "undefined" || o === null){
13332             return "null";
13333         }else if(o instanceof Array){
13334             return encodeArray(o);
13335         }else if(o instanceof Date){
13336             return encodeDate(o);
13337         }else if(typeof o == "string"){
13338             return encodeString(o);
13339         }else if(typeof o == "number"){
13340             return isFinite(o) ? String(o) : "null";
13341         }else if(typeof o == "boolean"){
13342             return String(o);
13343         }else {
13344             var a = ["{"], b, i, v;
13345             for (i in o) {
13346                 if(!useHasOwn || o.hasOwnProperty(i)) {
13347                     v = o[i];
13348                     switch (typeof v) {
13349                     case "undefined":
13350                     case "function":
13351                     case "unknown":
13352                         break;
13353                     default:
13354                         if(b){
13355                             a.push(',');
13356                         }
13357                         a.push(this.encode(i), ":",
13358                                 v === null ? "null" : this.encode(v));
13359                         b = true;
13360                     }
13361                 }
13362             }
13363             a.push("}");
13364             return a.join("");
13365         }
13366     };
13367     
13368     /**
13369      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13370      * @param {String} json The JSON string
13371      * @return {Object} The resulting object
13372      */
13373     this.decode = function(json){
13374         
13375         return  /** eval:var:json */ eval("(" + json + ')');
13376     };
13377 })();
13378 /** 
13379  * Shorthand for {@link Roo.util.JSON#encode}
13380  * @member Roo encode 
13381  * @method */
13382 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13383 /** 
13384  * Shorthand for {@link Roo.util.JSON#decode}
13385  * @member Roo decode 
13386  * @method */
13387 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13388 /*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398  
13399 /**
13400  * @class Roo.util.Format
13401  * Reusable data formatting functions
13402  * @singleton
13403  */
13404 Roo.util.Format = function(){
13405     var trimRe = /^\s+|\s+$/g;
13406     return {
13407         /**
13408          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13409          * @param {String} value The string to truncate
13410          * @param {Number} length The maximum length to allow before truncating
13411          * @return {String} The converted text
13412          */
13413         ellipsis : function(value, len){
13414             if(value && value.length > len){
13415                 return value.substr(0, len-3)+"...";
13416             }
13417             return value;
13418         },
13419
13420         /**
13421          * Checks a reference and converts it to empty string if it is undefined
13422          * @param {Mixed} value Reference to check
13423          * @return {Mixed} Empty string if converted, otherwise the original value
13424          */
13425         undef : function(value){
13426             return typeof value != "undefined" ? value : "";
13427         },
13428
13429         /**
13430          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13431          * @param {String} value The string to encode
13432          * @return {String} The encoded text
13433          */
13434         htmlEncode : function(value){
13435             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13436         },
13437
13438         /**
13439          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13440          * @param {String} value The string to decode
13441          * @return {String} The decoded text
13442          */
13443         htmlDecode : function(value){
13444             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13445         },
13446
13447         /**
13448          * Trims any whitespace from either side of a string
13449          * @param {String} value The text to trim
13450          * @return {String} The trimmed text
13451          */
13452         trim : function(value){
13453             return String(value).replace(trimRe, "");
13454         },
13455
13456         /**
13457          * Returns a substring from within an original string
13458          * @param {String} value The original text
13459          * @param {Number} start The start index of the substring
13460          * @param {Number} length The length of the substring
13461          * @return {String} The substring
13462          */
13463         substr : function(value, start, length){
13464             return String(value).substr(start, length);
13465         },
13466
13467         /**
13468          * Converts a string to all lower case letters
13469          * @param {String} value The text to convert
13470          * @return {String} The converted text
13471          */
13472         lowercase : function(value){
13473             return String(value).toLowerCase();
13474         },
13475
13476         /**
13477          * Converts a string to all upper case letters
13478          * @param {String} value The text to convert
13479          * @return {String} The converted text
13480          */
13481         uppercase : function(value){
13482             return String(value).toUpperCase();
13483         },
13484
13485         /**
13486          * Converts the first character only of a string to upper case
13487          * @param {String} value The text to convert
13488          * @return {String} The converted text
13489          */
13490         capitalize : function(value){
13491             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13492         },
13493
13494         // private
13495         call : function(value, fn){
13496             if(arguments.length > 2){
13497                 var args = Array.prototype.slice.call(arguments, 2);
13498                 args.unshift(value);
13499                  
13500                 return /** eval:var:value */  eval(fn).apply(window, args);
13501             }else{
13502                 /** eval:var:value */
13503                 return /** eval:var:value */ eval(fn).call(window, value);
13504             }
13505         },
13506
13507        
13508         /**
13509          * safer version of Math.toFixed..??/
13510          * @param {Number/String} value The numeric value to format
13511          * @param {Number/String} value Decimal places 
13512          * @return {String} The formatted currency string
13513          */
13514         toFixed : function(v, n)
13515         {
13516             // why not use to fixed - precision is buggered???
13517             if (!n) {
13518                 return Math.round(v-0);
13519             }
13520             var fact = Math.pow(10,n+1);
13521             v = (Math.round((v-0)*fact))/fact;
13522             var z = (''+fact).substring(2);
13523             if (v == Math.floor(v)) {
13524                 return Math.floor(v) + '.' + z;
13525             }
13526             
13527             // now just padd decimals..
13528             var ps = String(v).split('.');
13529             var fd = (ps[1] + z);
13530             var r = fd.substring(0,n); 
13531             var rm = fd.substring(n); 
13532             if (rm < 5) {
13533                 return ps[0] + '.' + r;
13534             }
13535             r*=1; // turn it into a number;
13536             r++;
13537             if (String(r).length != n) {
13538                 ps[0]*=1;
13539                 ps[0]++;
13540                 r = String(r).substring(1); // chop the end off.
13541             }
13542             
13543             return ps[0] + '.' + r;
13544              
13545         },
13546         
13547         /**
13548          * Format a number as US currency
13549          * @param {Number/String} value The numeric value to format
13550          * @return {String} The formatted currency string
13551          */
13552         usMoney : function(v){
13553             return '$' + Roo.util.Format.number(v);
13554         },
13555         
13556         /**
13557          * Format a number
13558          * eventually this should probably emulate php's number_format
13559          * @param {Number/String} value The numeric value to format
13560          * @param {Number} decimals number of decimal places
13561          * @return {String} The formatted currency string
13562          */
13563         number : function(v,decimals)
13564         {
13565             // multiply and round.
13566             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13567             var mul = Math.pow(10, decimals);
13568             var zero = String(mul).substring(1);
13569             v = (Math.round((v-0)*mul))/mul;
13570             
13571             // if it's '0' number.. then
13572             
13573             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13574             v = String(v);
13575             var ps = v.split('.');
13576             var whole = ps[0];
13577             
13578             
13579             var r = /(\d+)(\d{3})/;
13580             // add comma's
13581             while (r.test(whole)) {
13582                 whole = whole.replace(r, '$1' + ',' + '$2');
13583             }
13584             
13585             
13586             var sub = ps[1] ?
13587                     // has decimals..
13588                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13589                     // does not have decimals
13590                     (decimals ? ('.' + zero) : '');
13591             
13592             
13593             return whole + sub ;
13594         },
13595         
13596         /**
13597          * Parse a value into a formatted date using the specified format pattern.
13598          * @param {Mixed} value The value to format
13599          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13600          * @return {String} The formatted date string
13601          */
13602         date : function(v, format){
13603             if(!v){
13604                 return "";
13605             }
13606             if(!(v instanceof Date)){
13607                 v = new Date(Date.parse(v));
13608             }
13609             return v.dateFormat(format || Roo.util.Format.defaults.date);
13610         },
13611
13612         /**
13613          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13614          * @param {String} format Any valid date format string
13615          * @return {Function} The date formatting function
13616          */
13617         dateRenderer : function(format){
13618             return function(v){
13619                 return Roo.util.Format.date(v, format);  
13620             };
13621         },
13622
13623         // private
13624         stripTagsRE : /<\/?[^>]+>/gi,
13625         
13626         /**
13627          * Strips all HTML tags
13628          * @param {Mixed} value The text from which to strip tags
13629          * @return {String} The stripped text
13630          */
13631         stripTags : function(v){
13632             return !v ? v : String(v).replace(this.stripTagsRE, "");
13633         }
13634     };
13635 }();
13636 Roo.util.Format.defaults = {
13637     date : 'd/M/Y'
13638 };/*
13639  * Based on:
13640  * Ext JS Library 1.1.1
13641  * Copyright(c) 2006-2007, Ext JS, LLC.
13642  *
13643  * Originally Released Under LGPL - original licence link has changed is not relivant.
13644  *
13645  * Fork - LGPL
13646  * <script type="text/javascript">
13647  */
13648
13649
13650  
13651
13652 /**
13653  * @class Roo.MasterTemplate
13654  * @extends Roo.Template
13655  * Provides a template that can have child templates. The syntax is:
13656 <pre><code>
13657 var t = new Roo.MasterTemplate(
13658         '&lt;select name="{name}"&gt;',
13659                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13660         '&lt;/select&gt;'
13661 );
13662 t.add('options', {value: 'foo', text: 'bar'});
13663 // or you can add multiple child elements in one shot
13664 t.addAll('options', [
13665     {value: 'foo', text: 'bar'},
13666     {value: 'foo2', text: 'bar2'},
13667     {value: 'foo3', text: 'bar3'}
13668 ]);
13669 // then append, applying the master template values
13670 t.append('my-form', {name: 'my-select'});
13671 </code></pre>
13672 * A name attribute for the child template is not required if you have only one child
13673 * template or you want to refer to them by index.
13674  */
13675 Roo.MasterTemplate = function(){
13676     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13677     this.originalHtml = this.html;
13678     var st = {};
13679     var m, re = this.subTemplateRe;
13680     re.lastIndex = 0;
13681     var subIndex = 0;
13682     while(m = re.exec(this.html)){
13683         var name = m[1], content = m[2];
13684         st[subIndex] = {
13685             name: name,
13686             index: subIndex,
13687             buffer: [],
13688             tpl : new Roo.Template(content)
13689         };
13690         if(name){
13691             st[name] = st[subIndex];
13692         }
13693         st[subIndex].tpl.compile();
13694         st[subIndex].tpl.call = this.call.createDelegate(this);
13695         subIndex++;
13696     }
13697     this.subCount = subIndex;
13698     this.subs = st;
13699 };
13700 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13701     /**
13702     * The regular expression used to match sub templates
13703     * @type RegExp
13704     * @property
13705     */
13706     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13707
13708     /**
13709      * Applies the passed values to a child template.
13710      * @param {String/Number} name (optional) The name or index of the child template
13711      * @param {Array/Object} values The values to be applied to the template
13712      * @return {MasterTemplate} this
13713      */
13714      add : function(name, values){
13715         if(arguments.length == 1){
13716             values = arguments[0];
13717             name = 0;
13718         }
13719         var s = this.subs[name];
13720         s.buffer[s.buffer.length] = s.tpl.apply(values);
13721         return this;
13722     },
13723
13724     /**
13725      * Applies all the passed values to a child template.
13726      * @param {String/Number} name (optional) The name or index of the child template
13727      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13728      * @param {Boolean} reset (optional) True to reset the template first
13729      * @return {MasterTemplate} this
13730      */
13731     fill : function(name, values, reset){
13732         var a = arguments;
13733         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13734             values = a[0];
13735             name = 0;
13736             reset = a[1];
13737         }
13738         if(reset){
13739             this.reset();
13740         }
13741         for(var i = 0, len = values.length; i < len; i++){
13742             this.add(name, values[i]);
13743         }
13744         return this;
13745     },
13746
13747     /**
13748      * Resets the template for reuse
13749      * @return {MasterTemplate} this
13750      */
13751      reset : function(){
13752         var s = this.subs;
13753         for(var i = 0; i < this.subCount; i++){
13754             s[i].buffer = [];
13755         }
13756         return this;
13757     },
13758
13759     applyTemplate : function(values){
13760         var s = this.subs;
13761         var replaceIndex = -1;
13762         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13763             return s[++replaceIndex].buffer.join("");
13764         });
13765         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13766     },
13767
13768     apply : function(){
13769         return this.applyTemplate.apply(this, arguments);
13770     },
13771
13772     compile : function(){return this;}
13773 });
13774
13775 /**
13776  * Alias for fill().
13777  * @method
13778  */
13779 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13780  /**
13781  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13782  * var tpl = Roo.MasterTemplate.from('element-id');
13783  * @param {String/HTMLElement} el
13784  * @param {Object} config
13785  * @static
13786  */
13787 Roo.MasterTemplate.from = function(el, config){
13788     el = Roo.getDom(el);
13789     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13790 };/*
13791  * Based on:
13792  * Ext JS Library 1.1.1
13793  * Copyright(c) 2006-2007, Ext JS, LLC.
13794  *
13795  * Originally Released Under LGPL - original licence link has changed is not relivant.
13796  *
13797  * Fork - LGPL
13798  * <script type="text/javascript">
13799  */
13800
13801  
13802 /**
13803  * @class Roo.util.CSS
13804  * Utility class for manipulating CSS rules
13805  * @singleton
13806  */
13807 Roo.util.CSS = function(){
13808         var rules = null;
13809         var doc = document;
13810
13811     var camelRe = /(-[a-z])/gi;
13812     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13813
13814    return {
13815    /**
13816     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13817     * tag and appended to the HEAD of the document.
13818     * @param {String|Object} cssText The text containing the css rules
13819     * @param {String} id An id to add to the stylesheet for later removal
13820     * @return {StyleSheet}
13821     */
13822     createStyleSheet : function(cssText, id){
13823         var ss;
13824         var head = doc.getElementsByTagName("head")[0];
13825         var nrules = doc.createElement("style");
13826         nrules.setAttribute("type", "text/css");
13827         if(id){
13828             nrules.setAttribute("id", id);
13829         }
13830         if (typeof(cssText) != 'string') {
13831             // support object maps..
13832             // not sure if this a good idea.. 
13833             // perhaps it should be merged with the general css handling
13834             // and handle js style props.
13835             var cssTextNew = [];
13836             for(var n in cssText) {
13837                 var citems = [];
13838                 for(var k in cssText[n]) {
13839                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13840                 }
13841                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13842                 
13843             }
13844             cssText = cssTextNew.join("\n");
13845             
13846         }
13847        
13848        
13849        if(Roo.isIE){
13850            head.appendChild(nrules);
13851            ss = nrules.styleSheet;
13852            ss.cssText = cssText;
13853        }else{
13854            try{
13855                 nrules.appendChild(doc.createTextNode(cssText));
13856            }catch(e){
13857                nrules.cssText = cssText; 
13858            }
13859            head.appendChild(nrules);
13860            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13861        }
13862        this.cacheStyleSheet(ss);
13863        return ss;
13864    },
13865
13866    /**
13867     * Removes a style or link tag by id
13868     * @param {String} id The id of the tag
13869     */
13870    removeStyleSheet : function(id){
13871        var existing = doc.getElementById(id);
13872        if(existing){
13873            existing.parentNode.removeChild(existing);
13874        }
13875    },
13876
13877    /**
13878     * Dynamically swaps an existing stylesheet reference for a new one
13879     * @param {String} id The id of an existing link tag to remove
13880     * @param {String} url The href of the new stylesheet to include
13881     */
13882    swapStyleSheet : function(id, url){
13883        this.removeStyleSheet(id);
13884        var ss = doc.createElement("link");
13885        ss.setAttribute("rel", "stylesheet");
13886        ss.setAttribute("type", "text/css");
13887        ss.setAttribute("id", id);
13888        ss.setAttribute("href", url);
13889        doc.getElementsByTagName("head")[0].appendChild(ss);
13890    },
13891    
13892    /**
13893     * Refresh the rule cache if you have dynamically added stylesheets
13894     * @return {Object} An object (hash) of rules indexed by selector
13895     */
13896    refreshCache : function(){
13897        return this.getRules(true);
13898    },
13899
13900    // private
13901    cacheStyleSheet : function(stylesheet){
13902        if(!rules){
13903            rules = {};
13904        }
13905        try{// try catch for cross domain access issue
13906            var ssRules = stylesheet.cssRules || stylesheet.rules;
13907            for(var j = ssRules.length-1; j >= 0; --j){
13908                rules[ssRules[j].selectorText] = ssRules[j];
13909            }
13910        }catch(e){}
13911    },
13912    
13913    /**
13914     * Gets all css rules for the document
13915     * @param {Boolean} refreshCache true to refresh the internal cache
13916     * @return {Object} An object (hash) of rules indexed by selector
13917     */
13918    getRules : function(refreshCache){
13919                 if(rules == null || refreshCache){
13920                         rules = {};
13921                         var ds = doc.styleSheets;
13922                         for(var i =0, len = ds.length; i < len; i++){
13923                             try{
13924                         this.cacheStyleSheet(ds[i]);
13925                     }catch(e){} 
13926                 }
13927                 }
13928                 return rules;
13929         },
13930         
13931         /**
13932     * Gets an an individual CSS rule by selector(s)
13933     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13934     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13935     * @return {CSSRule} The CSS rule or null if one is not found
13936     */
13937    getRule : function(selector, refreshCache){
13938                 var rs = this.getRules(refreshCache);
13939                 if(!(selector instanceof Array)){
13940                     return rs[selector];
13941                 }
13942                 for(var i = 0; i < selector.length; i++){
13943                         if(rs[selector[i]]){
13944                                 return rs[selector[i]];
13945                         }
13946                 }
13947                 return null;
13948         },
13949         
13950         
13951         /**
13952     * Updates a rule property
13953     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13954     * @param {String} property The css property
13955     * @param {String} value The new value for the property
13956     * @return {Boolean} true If a rule was found and updated
13957     */
13958    updateRule : function(selector, property, value){
13959                 if(!(selector instanceof Array)){
13960                         var rule = this.getRule(selector);
13961                         if(rule){
13962                                 rule.style[property.replace(camelRe, camelFn)] = value;
13963                                 return true;
13964                         }
13965                 }else{
13966                         for(var i = 0; i < selector.length; i++){
13967                                 if(this.updateRule(selector[i], property, value)){
13968                                         return true;
13969                                 }
13970                         }
13971                 }
13972                 return false;
13973         }
13974    };   
13975 }();/*
13976  * Based on:
13977  * Ext JS Library 1.1.1
13978  * Copyright(c) 2006-2007, Ext JS, LLC.
13979  *
13980  * Originally Released Under LGPL - original licence link has changed is not relivant.
13981  *
13982  * Fork - LGPL
13983  * <script type="text/javascript">
13984  */
13985
13986  
13987
13988 /**
13989  * @class Roo.util.ClickRepeater
13990  * @extends Roo.util.Observable
13991  * 
13992  * A wrapper class which can be applied to any element. Fires a "click" event while the
13993  * mouse is pressed. The interval between firings may be specified in the config but
13994  * defaults to 10 milliseconds.
13995  * 
13996  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13997  * 
13998  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13999  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14000  * Similar to an autorepeat key delay.
14001  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14002  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14003  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14004  *           "interval" and "delay" are ignored. "immediate" is honored.
14005  * @cfg {Boolean} preventDefault True to prevent the default click event
14006  * @cfg {Boolean} stopDefault True to stop the default click event
14007  * 
14008  * @history
14009  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14010  *     2007-02-02 jvs Renamed to ClickRepeater
14011  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14012  *
14013  *  @constructor
14014  * @param {String/HTMLElement/Element} el The element to listen on
14015  * @param {Object} config
14016  **/
14017 Roo.util.ClickRepeater = function(el, config)
14018 {
14019     this.el = Roo.get(el);
14020     this.el.unselectable();
14021
14022     Roo.apply(this, config);
14023
14024     this.addEvents({
14025     /**
14026      * @event mousedown
14027      * Fires when the mouse button is depressed.
14028      * @param {Roo.util.ClickRepeater} this
14029      */
14030         "mousedown" : true,
14031     /**
14032      * @event click
14033      * Fires on a specified interval during the time the element is pressed.
14034      * @param {Roo.util.ClickRepeater} this
14035      */
14036         "click" : true,
14037     /**
14038      * @event mouseup
14039      * Fires when the mouse key is released.
14040      * @param {Roo.util.ClickRepeater} this
14041      */
14042         "mouseup" : true
14043     });
14044
14045     this.el.on("mousedown", this.handleMouseDown, this);
14046     if(this.preventDefault || this.stopDefault){
14047         this.el.on("click", function(e){
14048             if(this.preventDefault){
14049                 e.preventDefault();
14050             }
14051             if(this.stopDefault){
14052                 e.stopEvent();
14053             }
14054         }, this);
14055     }
14056
14057     // allow inline handler
14058     if(this.handler){
14059         this.on("click", this.handler,  this.scope || this);
14060     }
14061
14062     Roo.util.ClickRepeater.superclass.constructor.call(this);
14063 };
14064
14065 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14066     interval : 20,
14067     delay: 250,
14068     preventDefault : true,
14069     stopDefault : false,
14070     timer : 0,
14071
14072     // private
14073     handleMouseDown : function(){
14074         clearTimeout(this.timer);
14075         this.el.blur();
14076         if(this.pressClass){
14077             this.el.addClass(this.pressClass);
14078         }
14079         this.mousedownTime = new Date();
14080
14081         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14082         this.el.on("mouseout", this.handleMouseOut, this);
14083
14084         this.fireEvent("mousedown", this);
14085         this.fireEvent("click", this);
14086         
14087         this.timer = this.click.defer(this.delay || this.interval, this);
14088     },
14089
14090     // private
14091     click : function(){
14092         this.fireEvent("click", this);
14093         this.timer = this.click.defer(this.getInterval(), this);
14094     },
14095
14096     // private
14097     getInterval: function(){
14098         if(!this.accelerate){
14099             return this.interval;
14100         }
14101         var pressTime = this.mousedownTime.getElapsed();
14102         if(pressTime < 500){
14103             return 400;
14104         }else if(pressTime < 1700){
14105             return 320;
14106         }else if(pressTime < 2600){
14107             return 250;
14108         }else if(pressTime < 3500){
14109             return 180;
14110         }else if(pressTime < 4400){
14111             return 140;
14112         }else if(pressTime < 5300){
14113             return 80;
14114         }else if(pressTime < 6200){
14115             return 50;
14116         }else{
14117             return 10;
14118         }
14119     },
14120
14121     // private
14122     handleMouseOut : function(){
14123         clearTimeout(this.timer);
14124         if(this.pressClass){
14125             this.el.removeClass(this.pressClass);
14126         }
14127         this.el.on("mouseover", this.handleMouseReturn, this);
14128     },
14129
14130     // private
14131     handleMouseReturn : function(){
14132         this.el.un("mouseover", this.handleMouseReturn);
14133         if(this.pressClass){
14134             this.el.addClass(this.pressClass);
14135         }
14136         this.click();
14137     },
14138
14139     // private
14140     handleMouseUp : function(){
14141         clearTimeout(this.timer);
14142         this.el.un("mouseover", this.handleMouseReturn);
14143         this.el.un("mouseout", this.handleMouseOut);
14144         Roo.get(document).un("mouseup", this.handleMouseUp);
14145         this.el.removeClass(this.pressClass);
14146         this.fireEvent("mouseup", this);
14147     }
14148 });/*
14149  * Based on:
14150  * Ext JS Library 1.1.1
14151  * Copyright(c) 2006-2007, Ext JS, LLC.
14152  *
14153  * Originally Released Under LGPL - original licence link has changed is not relivant.
14154  *
14155  * Fork - LGPL
14156  * <script type="text/javascript">
14157  */
14158
14159  
14160 /**
14161  * @class Roo.KeyNav
14162  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14163  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14164  * way to implement custom navigation schemes for any UI component.</p>
14165  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14166  * pageUp, pageDown, del, home, end.  Usage:</p>
14167  <pre><code>
14168 var nav = new Roo.KeyNav("my-element", {
14169     "left" : function(e){
14170         this.moveLeft(e.ctrlKey);
14171     },
14172     "right" : function(e){
14173         this.moveRight(e.ctrlKey);
14174     },
14175     "enter" : function(e){
14176         this.save();
14177     },
14178     scope : this
14179 });
14180 </code></pre>
14181  * @constructor
14182  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14183  * @param {Object} config The config
14184  */
14185 Roo.KeyNav = function(el, config){
14186     this.el = Roo.get(el);
14187     Roo.apply(this, config);
14188     if(!this.disabled){
14189         this.disabled = true;
14190         this.enable();
14191     }
14192 };
14193
14194 Roo.KeyNav.prototype = {
14195     /**
14196      * @cfg {Boolean} disabled
14197      * True to disable this KeyNav instance (defaults to false)
14198      */
14199     disabled : false,
14200     /**
14201      * @cfg {String} defaultEventAction
14202      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14203      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14204      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14205      */
14206     defaultEventAction: "stopEvent",
14207     /**
14208      * @cfg {Boolean} forceKeyDown
14209      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14210      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14211      * handle keydown instead of keypress.
14212      */
14213     forceKeyDown : false,
14214
14215     // private
14216     prepareEvent : function(e){
14217         var k = e.getKey();
14218         var h = this.keyToHandler[k];
14219         //if(h && this[h]){
14220         //    e.stopPropagation();
14221         //}
14222         if(Roo.isSafari && h && k >= 37 && k <= 40){
14223             e.stopEvent();
14224         }
14225     },
14226
14227     // private
14228     relay : function(e){
14229         var k = e.getKey();
14230         var h = this.keyToHandler[k];
14231         if(h && this[h]){
14232             if(this.doRelay(e, this[h], h) !== true){
14233                 e[this.defaultEventAction]();
14234             }
14235         }
14236     },
14237
14238     // private
14239     doRelay : function(e, h, hname){
14240         return h.call(this.scope || this, e);
14241     },
14242
14243     // possible handlers
14244     enter : false,
14245     left : false,
14246     right : false,
14247     up : false,
14248     down : false,
14249     tab : false,
14250     esc : false,
14251     pageUp : false,
14252     pageDown : false,
14253     del : false,
14254     home : false,
14255     end : false,
14256
14257     // quick lookup hash
14258     keyToHandler : {
14259         37 : "left",
14260         39 : "right",
14261         38 : "up",
14262         40 : "down",
14263         33 : "pageUp",
14264         34 : "pageDown",
14265         46 : "del",
14266         36 : "home",
14267         35 : "end",
14268         13 : "enter",
14269         27 : "esc",
14270         9  : "tab"
14271     },
14272
14273         /**
14274          * Enable this KeyNav
14275          */
14276         enable: function(){
14277                 if(this.disabled){
14278             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14279             // the EventObject will normalize Safari automatically
14280             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14281                 this.el.on("keydown", this.relay,  this);
14282             }else{
14283                 this.el.on("keydown", this.prepareEvent,  this);
14284                 this.el.on("keypress", this.relay,  this);
14285             }
14286                     this.disabled = false;
14287                 }
14288         },
14289
14290         /**
14291          * Disable this KeyNav
14292          */
14293         disable: function(){
14294                 if(!this.disabled){
14295                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14296                 this.el.un("keydown", this.relay);
14297             }else{
14298                 this.el.un("keydown", this.prepareEvent);
14299                 this.el.un("keypress", this.relay);
14300             }
14301                     this.disabled = true;
14302                 }
14303         }
14304 };/*
14305  * Based on:
14306  * Ext JS Library 1.1.1
14307  * Copyright(c) 2006-2007, Ext JS, LLC.
14308  *
14309  * Originally Released Under LGPL - original licence link has changed is not relivant.
14310  *
14311  * Fork - LGPL
14312  * <script type="text/javascript">
14313  */
14314
14315  
14316 /**
14317  * @class Roo.KeyMap
14318  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14319  * The constructor accepts the same config object as defined by {@link #addBinding}.
14320  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14321  * combination it will call the function with this signature (if the match is a multi-key
14322  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14323  * A KeyMap can also handle a string representation of keys.<br />
14324  * Usage:
14325  <pre><code>
14326 // map one key by key code
14327 var map = new Roo.KeyMap("my-element", {
14328     key: 13, // or Roo.EventObject.ENTER
14329     fn: myHandler,
14330     scope: myObject
14331 });
14332
14333 // map multiple keys to one action by string
14334 var map = new Roo.KeyMap("my-element", {
14335     key: "a\r\n\t",
14336     fn: myHandler,
14337     scope: myObject
14338 });
14339
14340 // map multiple keys to multiple actions by strings and array of codes
14341 var map = new Roo.KeyMap("my-element", [
14342     {
14343         key: [10,13],
14344         fn: function(){ alert("Return was pressed"); }
14345     }, {
14346         key: "abc",
14347         fn: function(){ alert('a, b or c was pressed'); }
14348     }, {
14349         key: "\t",
14350         ctrl:true,
14351         shift:true,
14352         fn: function(){ alert('Control + shift + tab was pressed.'); }
14353     }
14354 ]);
14355 </code></pre>
14356  * <b>Note: A KeyMap starts enabled</b>
14357  * @constructor
14358  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14359  * @param {Object} config The config (see {@link #addBinding})
14360  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14361  */
14362 Roo.KeyMap = function(el, config, eventName){
14363     this.el  = Roo.get(el);
14364     this.eventName = eventName || "keydown";
14365     this.bindings = [];
14366     if(config){
14367         this.addBinding(config);
14368     }
14369     this.enable();
14370 };
14371
14372 Roo.KeyMap.prototype = {
14373     /**
14374      * True to stop the event from bubbling and prevent the default browser action if the
14375      * key was handled by the KeyMap (defaults to false)
14376      * @type Boolean
14377      */
14378     stopEvent : false,
14379
14380     /**
14381      * Add a new binding to this KeyMap. The following config object properties are supported:
14382      * <pre>
14383 Property    Type             Description
14384 ----------  ---------------  ----------------------------------------------------------------------
14385 key         String/Array     A single keycode or an array of keycodes to handle
14386 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14387 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14388 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14389 fn          Function         The function to call when KeyMap finds the expected key combination
14390 scope       Object           The scope of the callback function
14391 </pre>
14392      *
14393      * Usage:
14394      * <pre><code>
14395 // Create a KeyMap
14396 var map = new Roo.KeyMap(document, {
14397     key: Roo.EventObject.ENTER,
14398     fn: handleKey,
14399     scope: this
14400 });
14401
14402 //Add a new binding to the existing KeyMap later
14403 map.addBinding({
14404     key: 'abc',
14405     shift: true,
14406     fn: handleKey,
14407     scope: this
14408 });
14409 </code></pre>
14410      * @param {Object/Array} config A single KeyMap config or an array of configs
14411      */
14412         addBinding : function(config){
14413         if(config instanceof Array){
14414             for(var i = 0, len = config.length; i < len; i++){
14415                 this.addBinding(config[i]);
14416             }
14417             return;
14418         }
14419         var keyCode = config.key,
14420             shift = config.shift, 
14421             ctrl = config.ctrl, 
14422             alt = config.alt,
14423             fn = config.fn,
14424             scope = config.scope;
14425         if(typeof keyCode == "string"){
14426             var ks = [];
14427             var keyString = keyCode.toUpperCase();
14428             for(var j = 0, len = keyString.length; j < len; j++){
14429                 ks.push(keyString.charCodeAt(j));
14430             }
14431             keyCode = ks;
14432         }
14433         var keyArray = keyCode instanceof Array;
14434         var handler = function(e){
14435             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14436                 var k = e.getKey();
14437                 if(keyArray){
14438                     for(var i = 0, len = keyCode.length; i < len; i++){
14439                         if(keyCode[i] == k){
14440                           if(this.stopEvent){
14441                               e.stopEvent();
14442                           }
14443                           fn.call(scope || window, k, e);
14444                           return;
14445                         }
14446                     }
14447                 }else{
14448                     if(k == keyCode){
14449                         if(this.stopEvent){
14450                            e.stopEvent();
14451                         }
14452                         fn.call(scope || window, k, e);
14453                     }
14454                 }
14455             }
14456         };
14457         this.bindings.push(handler);  
14458         },
14459
14460     /**
14461      * Shorthand for adding a single key listener
14462      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14463      * following options:
14464      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14465      * @param {Function} fn The function to call
14466      * @param {Object} scope (optional) The scope of the function
14467      */
14468     on : function(key, fn, scope){
14469         var keyCode, shift, ctrl, alt;
14470         if(typeof key == "object" && !(key instanceof Array)){
14471             keyCode = key.key;
14472             shift = key.shift;
14473             ctrl = key.ctrl;
14474             alt = key.alt;
14475         }else{
14476             keyCode = key;
14477         }
14478         this.addBinding({
14479             key: keyCode,
14480             shift: shift,
14481             ctrl: ctrl,
14482             alt: alt,
14483             fn: fn,
14484             scope: scope
14485         })
14486     },
14487
14488     // private
14489     handleKeyDown : function(e){
14490             if(this.enabled){ //just in case
14491             var b = this.bindings;
14492             for(var i = 0, len = b.length; i < len; i++){
14493                 b[i].call(this, e);
14494             }
14495             }
14496         },
14497         
14498         /**
14499          * Returns true if this KeyMap is enabled
14500          * @return {Boolean} 
14501          */
14502         isEnabled : function(){
14503             return this.enabled;  
14504         },
14505         
14506         /**
14507          * Enables this KeyMap
14508          */
14509         enable: function(){
14510                 if(!this.enabled){
14511                     this.el.on(this.eventName, this.handleKeyDown, this);
14512                     this.enabled = true;
14513                 }
14514         },
14515
14516         /**
14517          * Disable this KeyMap
14518          */
14519         disable: function(){
14520                 if(this.enabled){
14521                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14522                     this.enabled = false;
14523                 }
14524         }
14525 };/*
14526  * Based on:
14527  * Ext JS Library 1.1.1
14528  * Copyright(c) 2006-2007, Ext JS, LLC.
14529  *
14530  * Originally Released Under LGPL - original licence link has changed is not relivant.
14531  *
14532  * Fork - LGPL
14533  * <script type="text/javascript">
14534  */
14535
14536  
14537 /**
14538  * @class Roo.util.TextMetrics
14539  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14540  * wide, in pixels, a given block of text will be.
14541  * @singleton
14542  */
14543 Roo.util.TextMetrics = function(){
14544     var shared;
14545     return {
14546         /**
14547          * Measures the size of the specified text
14548          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14549          * that can affect the size of the rendered text
14550          * @param {String} text The text to measure
14551          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14552          * in order to accurately measure the text height
14553          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14554          */
14555         measure : function(el, text, fixedWidth){
14556             if(!shared){
14557                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14558             }
14559             shared.bind(el);
14560             shared.setFixedWidth(fixedWidth || 'auto');
14561             return shared.getSize(text);
14562         },
14563
14564         /**
14565          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14566          * the overhead of multiple calls to initialize the style properties on each measurement.
14567          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14568          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14569          * in order to accurately measure the text height
14570          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14571          */
14572         createInstance : function(el, fixedWidth){
14573             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14574         }
14575     };
14576 }();
14577
14578  
14579
14580 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14581     var ml = new Roo.Element(document.createElement('div'));
14582     document.body.appendChild(ml.dom);
14583     ml.position('absolute');
14584     ml.setLeftTop(-1000, -1000);
14585     ml.hide();
14586
14587     if(fixedWidth){
14588         ml.setWidth(fixedWidth);
14589     }
14590      
14591     var instance = {
14592         /**
14593          * Returns the size of the specified text based on the internal element's style and width properties
14594          * @memberOf Roo.util.TextMetrics.Instance#
14595          * @param {String} text The text to measure
14596          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14597          */
14598         getSize : function(text){
14599             ml.update(text);
14600             var s = ml.getSize();
14601             ml.update('');
14602             return s;
14603         },
14604
14605         /**
14606          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14607          * that can affect the size of the rendered text
14608          * @memberOf Roo.util.TextMetrics.Instance#
14609          * @param {String/HTMLElement} el The element, dom node or id
14610          */
14611         bind : function(el){
14612             ml.setStyle(
14613                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14614             );
14615         },
14616
14617         /**
14618          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14619          * to set a fixed width in order to accurately measure the text height.
14620          * @memberOf Roo.util.TextMetrics.Instance#
14621          * @param {Number} width The width to set on the element
14622          */
14623         setFixedWidth : function(width){
14624             ml.setWidth(width);
14625         },
14626
14627         /**
14628          * Returns the measured width of the specified text
14629          * @memberOf Roo.util.TextMetrics.Instance#
14630          * @param {String} text The text to measure
14631          * @return {Number} width The width in pixels
14632          */
14633         getWidth : function(text){
14634             ml.dom.style.width = 'auto';
14635             return this.getSize(text).width;
14636         },
14637
14638         /**
14639          * Returns the measured height of the specified text.  For multiline text, be sure to call
14640          * {@link #setFixedWidth} if necessary.
14641          * @memberOf Roo.util.TextMetrics.Instance#
14642          * @param {String} text The text to measure
14643          * @return {Number} height The height in pixels
14644          */
14645         getHeight : function(text){
14646             return this.getSize(text).height;
14647         }
14648     };
14649
14650     instance.bind(bindTo);
14651
14652     return instance;
14653 };
14654
14655 // backwards compat
14656 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14657  * Based on:
14658  * Ext JS Library 1.1.1
14659  * Copyright(c) 2006-2007, Ext JS, LLC.
14660  *
14661  * Originally Released Under LGPL - original licence link has changed is not relivant.
14662  *
14663  * Fork - LGPL
14664  * <script type="text/javascript">
14665  */
14666
14667 /**
14668  * @class Roo.state.Provider
14669  * Abstract base class for state provider implementations. This class provides methods
14670  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14671  * Provider interface.
14672  */
14673 Roo.state.Provider = function(){
14674     /**
14675      * @event statechange
14676      * Fires when a state change occurs.
14677      * @param {Provider} this This state provider
14678      * @param {String} key The state key which was changed
14679      * @param {String} value The encoded value for the state
14680      */
14681     this.addEvents({
14682         "statechange": true
14683     });
14684     this.state = {};
14685     Roo.state.Provider.superclass.constructor.call(this);
14686 };
14687 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14688     /**
14689      * Returns the current value for a key
14690      * @param {String} name The key name
14691      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14692      * @return {Mixed} The state data
14693      */
14694     get : function(name, defaultValue){
14695         return typeof this.state[name] == "undefined" ?
14696             defaultValue : this.state[name];
14697     },
14698     
14699     /**
14700      * Clears a value from the state
14701      * @param {String} name The key name
14702      */
14703     clear : function(name){
14704         delete this.state[name];
14705         this.fireEvent("statechange", this, name, null);
14706     },
14707     
14708     /**
14709      * Sets the value for a key
14710      * @param {String} name The key name
14711      * @param {Mixed} value The value to set
14712      */
14713     set : function(name, value){
14714         this.state[name] = value;
14715         this.fireEvent("statechange", this, name, value);
14716     },
14717     
14718     /**
14719      * Decodes a string previously encoded with {@link #encodeValue}.
14720      * @param {String} value The value to decode
14721      * @return {Mixed} The decoded value
14722      */
14723     decodeValue : function(cookie){
14724         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14725         var matches = re.exec(unescape(cookie));
14726         if(!matches || !matches[1]) return; // non state cookie
14727         var type = matches[1];
14728         var v = matches[2];
14729         switch(type){
14730             case "n":
14731                 return parseFloat(v);
14732             case "d":
14733                 return new Date(Date.parse(v));
14734             case "b":
14735                 return (v == "1");
14736             case "a":
14737                 var all = [];
14738                 var values = v.split("^");
14739                 for(var i = 0, len = values.length; i < len; i++){
14740                     all.push(this.decodeValue(values[i]));
14741                 }
14742                 return all;
14743            case "o":
14744                 var all = {};
14745                 var values = v.split("^");
14746                 for(var i = 0, len = values.length; i < len; i++){
14747                     var kv = values[i].split("=");
14748                     all[kv[0]] = this.decodeValue(kv[1]);
14749                 }
14750                 return all;
14751            default:
14752                 return v;
14753         }
14754     },
14755     
14756     /**
14757      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14758      * @param {Mixed} value The value to encode
14759      * @return {String} The encoded value
14760      */
14761     encodeValue : function(v){
14762         var enc;
14763         if(typeof v == "number"){
14764             enc = "n:" + v;
14765         }else if(typeof v == "boolean"){
14766             enc = "b:" + (v ? "1" : "0");
14767         }else if(v instanceof Date){
14768             enc = "d:" + v.toGMTString();
14769         }else if(v instanceof Array){
14770             var flat = "";
14771             for(var i = 0, len = v.length; i < len; i++){
14772                 flat += this.encodeValue(v[i]);
14773                 if(i != len-1) flat += "^";
14774             }
14775             enc = "a:" + flat;
14776         }else if(typeof v == "object"){
14777             var flat = "";
14778             for(var key in v){
14779                 if(typeof v[key] != "function"){
14780                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14781                 }
14782             }
14783             enc = "o:" + flat.substring(0, flat.length-1);
14784         }else{
14785             enc = "s:" + v;
14786         }
14787         return escape(enc);        
14788     }
14789 });
14790
14791 /*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801 /**
14802  * @class Roo.state.Manager
14803  * This is the global state manager. By default all components that are "state aware" check this class
14804  * for state information if you don't pass them a custom state provider. In order for this class
14805  * to be useful, it must be initialized with a provider when your application initializes.
14806  <pre><code>
14807 // in your initialization function
14808 init : function(){
14809    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14810    ...
14811    // supposed you have a {@link Roo.BorderLayout}
14812    var layout = new Roo.BorderLayout(...);
14813    layout.restoreState();
14814    // or a {Roo.BasicDialog}
14815    var dialog = new Roo.BasicDialog(...);
14816    dialog.restoreState();
14817  </code></pre>
14818  * @singleton
14819  */
14820 Roo.state.Manager = function(){
14821     var provider = new Roo.state.Provider();
14822     
14823     return {
14824         /**
14825          * Configures the default state provider for your application
14826          * @param {Provider} stateProvider The state provider to set
14827          */
14828         setProvider : function(stateProvider){
14829             provider = stateProvider;
14830         },
14831         
14832         /**
14833          * Returns the current value for a key
14834          * @param {String} name The key name
14835          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14836          * @return {Mixed} The state data
14837          */
14838         get : function(key, defaultValue){
14839             return provider.get(key, defaultValue);
14840         },
14841         
14842         /**
14843          * Sets the value for a key
14844          * @param {String} name The key name
14845          * @param {Mixed} value The state data
14846          */
14847          set : function(key, value){
14848             provider.set(key, value);
14849         },
14850         
14851         /**
14852          * Clears a value from the state
14853          * @param {String} name The key name
14854          */
14855         clear : function(key){
14856             provider.clear(key);
14857         },
14858         
14859         /**
14860          * Gets the currently configured state provider
14861          * @return {Provider} The state provider
14862          */
14863         getProvider : function(){
14864             return provider;
14865         }
14866     };
14867 }();
14868 /*
14869  * Based on:
14870  * Ext JS Library 1.1.1
14871  * Copyright(c) 2006-2007, Ext JS, LLC.
14872  *
14873  * Originally Released Under LGPL - original licence link has changed is not relivant.
14874  *
14875  * Fork - LGPL
14876  * <script type="text/javascript">
14877  */
14878 /**
14879  * @class Roo.state.CookieProvider
14880  * @extends Roo.state.Provider
14881  * The default Provider implementation which saves state via cookies.
14882  * <br />Usage:
14883  <pre><code>
14884    var cp = new Roo.state.CookieProvider({
14885        path: "/cgi-bin/",
14886        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14887        domain: "roojs.com"
14888    })
14889    Roo.state.Manager.setProvider(cp);
14890  </code></pre>
14891  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14892  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14893  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14894  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14895  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14896  * domain the page is running on including the 'www' like 'www.roojs.com')
14897  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14898  * @constructor
14899  * Create a new CookieProvider
14900  * @param {Object} config The configuration object
14901  */
14902 Roo.state.CookieProvider = function(config){
14903     Roo.state.CookieProvider.superclass.constructor.call(this);
14904     this.path = "/";
14905     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14906     this.domain = null;
14907     this.secure = false;
14908     Roo.apply(this, config);
14909     this.state = this.readCookies();
14910 };
14911
14912 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14913     // private
14914     set : function(name, value){
14915         if(typeof value == "undefined" || value === null){
14916             this.clear(name);
14917             return;
14918         }
14919         this.setCookie(name, value);
14920         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14921     },
14922
14923     // private
14924     clear : function(name){
14925         this.clearCookie(name);
14926         Roo.state.CookieProvider.superclass.clear.call(this, name);
14927     },
14928
14929     // private
14930     readCookies : function(){
14931         var cookies = {};
14932         var c = document.cookie + ";";
14933         var re = /\s?(.*?)=(.*?);/g;
14934         var matches;
14935         while((matches = re.exec(c)) != null){
14936             var name = matches[1];
14937             var value = matches[2];
14938             if(name && name.substring(0,3) == "ys-"){
14939                 cookies[name.substr(3)] = this.decodeValue(value);
14940             }
14941         }
14942         return cookies;
14943     },
14944
14945     // private
14946     setCookie : function(name, value){
14947         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14948            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14949            ((this.path == null) ? "" : ("; path=" + this.path)) +
14950            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14951            ((this.secure == true) ? "; secure" : "");
14952     },
14953
14954     // private
14955     clearCookie : function(name){
14956         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14957            ((this.path == null) ? "" : ("; path=" + this.path)) +
14958            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14959            ((this.secure == true) ? "; secure" : "");
14960     }
14961 });/*
14962  * Based on:
14963  * Ext JS Library 1.1.1
14964  * Copyright(c) 2006-2007, Ext JS, LLC.
14965  *
14966  * Originally Released Under LGPL - original licence link has changed is not relivant.
14967  *
14968  * Fork - LGPL
14969  * <script type="text/javascript">
14970  */
14971  
14972
14973 /**
14974  * @class Roo.ComponentMgr
14975  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14976  * @singleton
14977  */
14978 Roo.ComponentMgr = function(){
14979     var all = new Roo.util.MixedCollection();
14980
14981     return {
14982         /**
14983          * Registers a component.
14984          * @param {Roo.Component} c The component
14985          */
14986         register : function(c){
14987             all.add(c);
14988         },
14989
14990         /**
14991          * Unregisters a component.
14992          * @param {Roo.Component} c The component
14993          */
14994         unregister : function(c){
14995             all.remove(c);
14996         },
14997
14998         /**
14999          * Returns a component by id
15000          * @param {String} id The component id
15001          */
15002         get : function(id){
15003             return all.get(id);
15004         },
15005
15006         /**
15007          * Registers a function that will be called when a specified component is added to ComponentMgr
15008          * @param {String} id The component id
15009          * @param {Funtction} fn The callback function
15010          * @param {Object} scope The scope of the callback
15011          */
15012         onAvailable : function(id, fn, scope){
15013             all.on("add", function(index, o){
15014                 if(o.id == id){
15015                     fn.call(scope || o, o);
15016                     all.un("add", fn, scope);
15017                 }
15018             });
15019         }
15020     };
15021 }();/*
15022  * Based on:
15023  * Ext JS Library 1.1.1
15024  * Copyright(c) 2006-2007, Ext JS, LLC.
15025  *
15026  * Originally Released Under LGPL - original licence link has changed is not relivant.
15027  *
15028  * Fork - LGPL
15029  * <script type="text/javascript">
15030  */
15031  
15032 /**
15033  * @class Roo.Component
15034  * @extends Roo.util.Observable
15035  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15036  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15037  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15038  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15039  * All visual components (widgets) that require rendering into a layout should subclass Component.
15040  * @constructor
15041  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15042  * 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
15043  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15044  */
15045 Roo.Component = function(config){
15046     config = config || {};
15047     if(config.tagName || config.dom || typeof config == "string"){ // element object
15048         config = {el: config, id: config.id || config};
15049     }
15050     this.initialConfig = config;
15051
15052     Roo.apply(this, config);
15053     this.addEvents({
15054         /**
15055          * @event disable
15056          * Fires after the component is disabled.
15057              * @param {Roo.Component} this
15058              */
15059         disable : true,
15060         /**
15061          * @event enable
15062          * Fires after the component is enabled.
15063              * @param {Roo.Component} this
15064              */
15065         enable : true,
15066         /**
15067          * @event beforeshow
15068          * Fires before the component is shown.  Return false to stop the show.
15069              * @param {Roo.Component} this
15070              */
15071         beforeshow : true,
15072         /**
15073          * @event show
15074          * Fires after the component is shown.
15075              * @param {Roo.Component} this
15076              */
15077         show : true,
15078         /**
15079          * @event beforehide
15080          * Fires before the component is hidden. Return false to stop the hide.
15081              * @param {Roo.Component} this
15082              */
15083         beforehide : true,
15084         /**
15085          * @event hide
15086          * Fires after the component is hidden.
15087              * @param {Roo.Component} this
15088              */
15089         hide : true,
15090         /**
15091          * @event beforerender
15092          * Fires before the component is rendered. Return false to stop the render.
15093              * @param {Roo.Component} this
15094              */
15095         beforerender : true,
15096         /**
15097          * @event render
15098          * Fires after the component is rendered.
15099              * @param {Roo.Component} this
15100              */
15101         render : true,
15102         /**
15103          * @event beforedestroy
15104          * Fires before the component is destroyed. Return false to stop the destroy.
15105              * @param {Roo.Component} this
15106              */
15107         beforedestroy : true,
15108         /**
15109          * @event destroy
15110          * Fires after the component is destroyed.
15111              * @param {Roo.Component} this
15112              */
15113         destroy : true
15114     });
15115     if(!this.id){
15116         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15117     }
15118     Roo.ComponentMgr.register(this);
15119     Roo.Component.superclass.constructor.call(this);
15120     this.initComponent();
15121     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15122         this.render(this.renderTo);
15123         delete this.renderTo;
15124     }
15125 };
15126
15127 /** @private */
15128 Roo.Component.AUTO_ID = 1000;
15129
15130 Roo.extend(Roo.Component, Roo.util.Observable, {
15131     /**
15132      * @scope Roo.Component.prototype
15133      * @type {Boolean}
15134      * true if this component is hidden. Read-only.
15135      */
15136     hidden : false,
15137     /**
15138      * @type {Boolean}
15139      * true if this component is disabled. Read-only.
15140      */
15141     disabled : false,
15142     /**
15143      * @type {Boolean}
15144      * true if this component has been rendered. Read-only.
15145      */
15146     rendered : false,
15147     
15148     /** @cfg {String} disableClass
15149      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15150      */
15151     disabledClass : "x-item-disabled",
15152         /** @cfg {Boolean} allowDomMove
15153          * Whether the component can move the Dom node when rendering (defaults to true).
15154          */
15155     allowDomMove : true,
15156     /** @cfg {String} hideMode
15157      * How this component should hidden. Supported values are
15158      * "visibility" (css visibility), "offsets" (negative offset position) and
15159      * "display" (css display) - defaults to "display".
15160      */
15161     hideMode: 'display',
15162
15163     /** @private */
15164     ctype : "Roo.Component",
15165
15166     /**
15167      * @cfg {String} actionMode 
15168      * which property holds the element that used for  hide() / show() / disable() / enable()
15169      * default is 'el' 
15170      */
15171     actionMode : "el",
15172
15173     /** @private */
15174     getActionEl : function(){
15175         return this[this.actionMode];
15176     },
15177
15178     initComponent : Roo.emptyFn,
15179     /**
15180      * If this is a lazy rendering component, render it to its container element.
15181      * @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.
15182      */
15183     render : function(container, position){
15184         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15185             if(!container && this.el){
15186                 this.el = Roo.get(this.el);
15187                 container = this.el.dom.parentNode;
15188                 this.allowDomMove = false;
15189             }
15190             this.container = Roo.get(container);
15191             this.rendered = true;
15192             if(position !== undefined){
15193                 if(typeof position == 'number'){
15194                     position = this.container.dom.childNodes[position];
15195                 }else{
15196                     position = Roo.getDom(position);
15197                 }
15198             }
15199             this.onRender(this.container, position || null);
15200             if(this.cls){
15201                 this.el.addClass(this.cls);
15202                 delete this.cls;
15203             }
15204             if(this.style){
15205                 this.el.applyStyles(this.style);
15206                 delete this.style;
15207             }
15208             this.fireEvent("render", this);
15209             this.afterRender(this.container);
15210             if(this.hidden){
15211                 this.hide();
15212             }
15213             if(this.disabled){
15214                 this.disable();
15215             }
15216         }
15217         return this;
15218     },
15219
15220     /** @private */
15221     // default function is not really useful
15222     onRender : function(ct, position){
15223         if(this.el){
15224             this.el = Roo.get(this.el);
15225             if(this.allowDomMove !== false){
15226                 ct.dom.insertBefore(this.el.dom, position);
15227             }
15228         }
15229     },
15230
15231     /** @private */
15232     getAutoCreate : function(){
15233         var cfg = typeof this.autoCreate == "object" ?
15234                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15235         if(this.id && !cfg.id){
15236             cfg.id = this.id;
15237         }
15238         return cfg;
15239     },
15240
15241     /** @private */
15242     afterRender : Roo.emptyFn,
15243
15244     /**
15245      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15246      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15247      */
15248     destroy : function(){
15249         if(this.fireEvent("beforedestroy", this) !== false){
15250             this.purgeListeners();
15251             this.beforeDestroy();
15252             if(this.rendered){
15253                 this.el.removeAllListeners();
15254                 this.el.remove();
15255                 if(this.actionMode == "container"){
15256                     this.container.remove();
15257                 }
15258             }
15259             this.onDestroy();
15260             Roo.ComponentMgr.unregister(this);
15261             this.fireEvent("destroy", this);
15262         }
15263     },
15264
15265         /** @private */
15266     beforeDestroy : function(){
15267
15268     },
15269
15270         /** @private */
15271         onDestroy : function(){
15272
15273     },
15274
15275     /**
15276      * Returns the underlying {@link Roo.Element}.
15277      * @return {Roo.Element} The element
15278      */
15279     getEl : function(){
15280         return this.el;
15281     },
15282
15283     /**
15284      * Returns the id of this component.
15285      * @return {String}
15286      */
15287     getId : function(){
15288         return this.id;
15289     },
15290
15291     /**
15292      * Try to focus this component.
15293      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15294      * @return {Roo.Component} this
15295      */
15296     focus : function(selectText){
15297         if(this.rendered){
15298             this.el.focus();
15299             if(selectText === true){
15300                 this.el.dom.select();
15301             }
15302         }
15303         return this;
15304     },
15305
15306     /** @private */
15307     blur : function(){
15308         if(this.rendered){
15309             this.el.blur();
15310         }
15311         return this;
15312     },
15313
15314     /**
15315      * Disable this component.
15316      * @return {Roo.Component} this
15317      */
15318     disable : function(){
15319         if(this.rendered){
15320             this.onDisable();
15321         }
15322         this.disabled = true;
15323         this.fireEvent("disable", this);
15324         return this;
15325     },
15326
15327         // private
15328     onDisable : function(){
15329         this.getActionEl().addClass(this.disabledClass);
15330         this.el.dom.disabled = true;
15331     },
15332
15333     /**
15334      * Enable this component.
15335      * @return {Roo.Component} this
15336      */
15337     enable : function(){
15338         if(this.rendered){
15339             this.onEnable();
15340         }
15341         this.disabled = false;
15342         this.fireEvent("enable", this);
15343         return this;
15344     },
15345
15346         // private
15347     onEnable : function(){
15348         this.getActionEl().removeClass(this.disabledClass);
15349         this.el.dom.disabled = false;
15350     },
15351
15352     /**
15353      * Convenience function for setting disabled/enabled by boolean.
15354      * @param {Boolean} disabled
15355      */
15356     setDisabled : function(disabled){
15357         this[disabled ? "disable" : "enable"]();
15358     },
15359
15360     /**
15361      * Show this component.
15362      * @return {Roo.Component} this
15363      */
15364     show: function(){
15365         if(this.fireEvent("beforeshow", this) !== false){
15366             this.hidden = false;
15367             if(this.rendered){
15368                 this.onShow();
15369             }
15370             this.fireEvent("show", this);
15371         }
15372         return this;
15373     },
15374
15375     // private
15376     onShow : function(){
15377         var ae = this.getActionEl();
15378         if(this.hideMode == 'visibility'){
15379             ae.dom.style.visibility = "visible";
15380         }else if(this.hideMode == 'offsets'){
15381             ae.removeClass('x-hidden');
15382         }else{
15383             ae.dom.style.display = "";
15384         }
15385     },
15386
15387     /**
15388      * Hide this component.
15389      * @return {Roo.Component} this
15390      */
15391     hide: function(){
15392         if(this.fireEvent("beforehide", this) !== false){
15393             this.hidden = true;
15394             if(this.rendered){
15395                 this.onHide();
15396             }
15397             this.fireEvent("hide", this);
15398         }
15399         return this;
15400     },
15401
15402     // private
15403     onHide : function(){
15404         var ae = this.getActionEl();
15405         if(this.hideMode == 'visibility'){
15406             ae.dom.style.visibility = "hidden";
15407         }else if(this.hideMode == 'offsets'){
15408             ae.addClass('x-hidden');
15409         }else{
15410             ae.dom.style.display = "none";
15411         }
15412     },
15413
15414     /**
15415      * Convenience function to hide or show this component by boolean.
15416      * @param {Boolean} visible True to show, false to hide
15417      * @return {Roo.Component} this
15418      */
15419     setVisible: function(visible){
15420         if(visible) {
15421             this.show();
15422         }else{
15423             this.hide();
15424         }
15425         return this;
15426     },
15427
15428     /**
15429      * Returns true if this component is visible.
15430      */
15431     isVisible : function(){
15432         return this.getActionEl().isVisible();
15433     },
15434
15435     cloneConfig : function(overrides){
15436         overrides = overrides || {};
15437         var id = overrides.id || Roo.id();
15438         var cfg = Roo.applyIf(overrides, this.initialConfig);
15439         cfg.id = id; // prevent dup id
15440         return new this.constructor(cfg);
15441     }
15442 });/*
15443  * Based on:
15444  * Ext JS Library 1.1.1
15445  * Copyright(c) 2006-2007, Ext JS, LLC.
15446  *
15447  * Originally Released Under LGPL - original licence link has changed is not relivant.
15448  *
15449  * Fork - LGPL
15450  * <script type="text/javascript">
15451  */
15452
15453 /**
15454  * @class Roo.BoxComponent
15455  * @extends Roo.Component
15456  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15457  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15458  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15459  * layout containers.
15460  * @constructor
15461  * @param {Roo.Element/String/Object} config The configuration options.
15462  */
15463 Roo.BoxComponent = function(config){
15464     Roo.Component.call(this, config);
15465     this.addEvents({
15466         /**
15467          * @event resize
15468          * Fires after the component is resized.
15469              * @param {Roo.Component} this
15470              * @param {Number} adjWidth The box-adjusted width that was set
15471              * @param {Number} adjHeight The box-adjusted height that was set
15472              * @param {Number} rawWidth The width that was originally specified
15473              * @param {Number} rawHeight The height that was originally specified
15474              */
15475         resize : true,
15476         /**
15477          * @event move
15478          * Fires after the component is moved.
15479              * @param {Roo.Component} this
15480              * @param {Number} x The new x position
15481              * @param {Number} y The new y position
15482              */
15483         move : true
15484     });
15485 };
15486
15487 Roo.extend(Roo.BoxComponent, Roo.Component, {
15488     // private, set in afterRender to signify that the component has been rendered
15489     boxReady : false,
15490     // private, used to defer height settings to subclasses
15491     deferHeight: false,
15492     /** @cfg {Number} width
15493      * width (optional) size of component
15494      */
15495      /** @cfg {Number} height
15496      * height (optional) size of component
15497      */
15498      
15499     /**
15500      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15501      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15502      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15503      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15504      * @return {Roo.BoxComponent} this
15505      */
15506     setSize : function(w, h){
15507         // support for standard size objects
15508         if(typeof w == 'object'){
15509             h = w.height;
15510             w = w.width;
15511         }
15512         // not rendered
15513         if(!this.boxReady){
15514             this.width = w;
15515             this.height = h;
15516             return this;
15517         }
15518
15519         // prevent recalcs when not needed
15520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15521             return this;
15522         }
15523         this.lastSize = {width: w, height: h};
15524
15525         var adj = this.adjustSize(w, h);
15526         var aw = adj.width, ah = adj.height;
15527         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15528             var rz = this.getResizeEl();
15529             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15530                 rz.setSize(aw, ah);
15531             }else if(!this.deferHeight && ah !== undefined){
15532                 rz.setHeight(ah);
15533             }else if(aw !== undefined){
15534                 rz.setWidth(aw);
15535             }
15536             this.onResize(aw, ah, w, h);
15537             this.fireEvent('resize', this, aw, ah, w, h);
15538         }
15539         return this;
15540     },
15541
15542     /**
15543      * Gets the current size of the component's underlying element.
15544      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15545      */
15546     getSize : function(){
15547         return this.el.getSize();
15548     },
15549
15550     /**
15551      * Gets the current XY position of the component's underlying element.
15552      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15553      * @return {Array} The XY position of the element (e.g., [100, 200])
15554      */
15555     getPosition : function(local){
15556         if(local === true){
15557             return [this.el.getLeft(true), this.el.getTop(true)];
15558         }
15559         return this.xy || this.el.getXY();
15560     },
15561
15562     /**
15563      * Gets the current box measurements of the component's underlying element.
15564      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15565      * @returns {Object} box An object in the format {x, y, width, height}
15566      */
15567     getBox : function(local){
15568         var s = this.el.getSize();
15569         if(local){
15570             s.x = this.el.getLeft(true);
15571             s.y = this.el.getTop(true);
15572         }else{
15573             var xy = this.xy || this.el.getXY();
15574             s.x = xy[0];
15575             s.y = xy[1];
15576         }
15577         return s;
15578     },
15579
15580     /**
15581      * Sets the current box measurements of the component's underlying element.
15582      * @param {Object} box An object in the format {x, y, width, height}
15583      * @returns {Roo.BoxComponent} this
15584      */
15585     updateBox : function(box){
15586         this.setSize(box.width, box.height);
15587         this.setPagePosition(box.x, box.y);
15588         return this;
15589     },
15590
15591     // protected
15592     getResizeEl : function(){
15593         return this.resizeEl || this.el;
15594     },
15595
15596     // protected
15597     getPositionEl : function(){
15598         return this.positionEl || this.el;
15599     },
15600
15601     /**
15602      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15603      * This method fires the move event.
15604      * @param {Number} left The new left
15605      * @param {Number} top The new top
15606      * @returns {Roo.BoxComponent} this
15607      */
15608     setPosition : function(x, y){
15609         this.x = x;
15610         this.y = y;
15611         if(!this.boxReady){
15612             return this;
15613         }
15614         var adj = this.adjustPosition(x, y);
15615         var ax = adj.x, ay = adj.y;
15616
15617         var el = this.getPositionEl();
15618         if(ax !== undefined || ay !== undefined){
15619             if(ax !== undefined && ay !== undefined){
15620                 el.setLeftTop(ax, ay);
15621             }else if(ax !== undefined){
15622                 el.setLeft(ax);
15623             }else if(ay !== undefined){
15624                 el.setTop(ay);
15625             }
15626             this.onPosition(ax, ay);
15627             this.fireEvent('move', this, ax, ay);
15628         }
15629         return this;
15630     },
15631
15632     /**
15633      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15634      * This method fires the move event.
15635      * @param {Number} x The new x position
15636      * @param {Number} y The new y position
15637      * @returns {Roo.BoxComponent} this
15638      */
15639     setPagePosition : function(x, y){
15640         this.pageX = x;
15641         this.pageY = y;
15642         if(!this.boxReady){
15643             return;
15644         }
15645         if(x === undefined || y === undefined){ // cannot translate undefined points
15646             return;
15647         }
15648         var p = this.el.translatePoints(x, y);
15649         this.setPosition(p.left, p.top);
15650         return this;
15651     },
15652
15653     // private
15654     onRender : function(ct, position){
15655         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15656         if(this.resizeEl){
15657             this.resizeEl = Roo.get(this.resizeEl);
15658         }
15659         if(this.positionEl){
15660             this.positionEl = Roo.get(this.positionEl);
15661         }
15662     },
15663
15664     // private
15665     afterRender : function(){
15666         Roo.BoxComponent.superclass.afterRender.call(this);
15667         this.boxReady = true;
15668         this.setSize(this.width, this.height);
15669         if(this.x || this.y){
15670             this.setPosition(this.x, this.y);
15671         }
15672         if(this.pageX || this.pageY){
15673             this.setPagePosition(this.pageX, this.pageY);
15674         }
15675     },
15676
15677     /**
15678      * Force the component's size to recalculate based on the underlying element's current height and width.
15679      * @returns {Roo.BoxComponent} this
15680      */
15681     syncSize : function(){
15682         delete this.lastSize;
15683         this.setSize(this.el.getWidth(), this.el.getHeight());
15684         return this;
15685     },
15686
15687     /**
15688      * Called after the component is resized, this method is empty by default but can be implemented by any
15689      * subclass that needs to perform custom logic after a resize occurs.
15690      * @param {Number} adjWidth The box-adjusted width that was set
15691      * @param {Number} adjHeight The box-adjusted height that was set
15692      * @param {Number} rawWidth The width that was originally specified
15693      * @param {Number} rawHeight The height that was originally specified
15694      */
15695     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15696
15697     },
15698
15699     /**
15700      * Called after the component is moved, this method is empty by default but can be implemented by any
15701      * subclass that needs to perform custom logic after a move occurs.
15702      * @param {Number} x The new x position
15703      * @param {Number} y The new y position
15704      */
15705     onPosition : function(x, y){
15706
15707     },
15708
15709     // private
15710     adjustSize : function(w, h){
15711         if(this.autoWidth){
15712             w = 'auto';
15713         }
15714         if(this.autoHeight){
15715             h = 'auto';
15716         }
15717         return {width : w, height: h};
15718     },
15719
15720     // private
15721     adjustPosition : function(x, y){
15722         return {x : x, y: y};
15723     }
15724 });/*
15725  * Original code for Roojs - LGPL
15726  * <script type="text/javascript">
15727  */
15728  
15729 /**
15730  * @class Roo.XComponent
15731  * A delayed Element creator...
15732  * Or a way to group chunks of interface together.
15733  * 
15734  * Mypart.xyx = new Roo.XComponent({
15735
15736     parent : 'Mypart.xyz', // empty == document.element.!!
15737     order : '001',
15738     name : 'xxxx'
15739     region : 'xxxx'
15740     disabled : function() {} 
15741      
15742     tree : function() { // return an tree of xtype declared components
15743         var MODULE = this;
15744         return 
15745         {
15746             xtype : 'NestedLayoutPanel',
15747             // technicall
15748         }
15749      ]
15750  *})
15751  *
15752  *
15753  * It can be used to build a big heiracy, with parent etc.
15754  * or you can just use this to render a single compoent to a dom element
15755  * MYPART.render(Roo.Element | String(id) | dom_element )
15756  * 
15757  * @extends Roo.util.Observable
15758  * @constructor
15759  * @param cfg {Object} configuration of component
15760  * 
15761  */
15762 Roo.XComponent = function(cfg) {
15763     Roo.apply(this, cfg);
15764     this.addEvents({ 
15765         /**
15766              * @event built
15767              * Fires when this the componnt is built
15768              * @param {Roo.XComponent} c the component
15769              */
15770         'built' : true
15771         
15772     });
15773     this.region = this.region || 'center'; // default..
15774     Roo.XComponent.register(this);
15775     this.modules = false;
15776     this.el = false; // where the layout goes..
15777     
15778     
15779 }
15780 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15781     /**
15782      * @property el
15783      * The created element (with Roo.factory())
15784      * @type {Roo.Layout}
15785      */
15786     el  : false,
15787     
15788     /**
15789      * @property el
15790      * for BC  - use el in new code
15791      * @type {Roo.Layout}
15792      */
15793     panel : false,
15794     
15795     /**
15796      * @property layout
15797      * for BC  - use el in new code
15798      * @type {Roo.Layout}
15799      */
15800     layout : false,
15801     
15802      /**
15803      * @cfg {Function|boolean} disabled
15804      * If this module is disabled by some rule, return true from the funtion
15805      */
15806     disabled : false,
15807     
15808     /**
15809      * @cfg {String} parent 
15810      * Name of parent element which it get xtype added to..
15811      */
15812     parent: false,
15813     
15814     /**
15815      * @cfg {String} order
15816      * Used to set the order in which elements are created (usefull for multiple tabs)
15817      */
15818     
15819     order : false,
15820     /**
15821      * @cfg {String} name
15822      * String to display while loading.
15823      */
15824     name : false,
15825     /**
15826      * @cfg {String} region
15827      * Region to render component to (defaults to center)
15828      */
15829     region : 'center',
15830     
15831     /**
15832      * @cfg {Array} items
15833      * A single item array - the first element is the root of the tree..
15834      * It's done this way to stay compatible with the Xtype system...
15835      */
15836     items : false,
15837     
15838     /**
15839      * @property _tree
15840      * The method that retuns the tree of parts that make up this compoennt 
15841      * @type {function}
15842      */
15843     _tree  : false,
15844     
15845      /**
15846      * render
15847      * render element to dom or tree
15848      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15849      */
15850     
15851     render : function(el)
15852     {
15853         
15854         el = el || false;
15855         var hp = this.parent ? 1 : 0;
15856         
15857         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15858             // if parent is a '#.....' string, then let's use that..
15859             var ename = this.parent.substr(1)
15860             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15861             el = Roo.get(ename);
15862             if (!el && !this.parent) {
15863                 Roo.log("Warning - element can not be found :#" + ename );
15864                 return;
15865             }
15866         }
15867         var tree = this._tree ? this._tree() : this.tree();
15868
15869         
15870         if (!this.parent && typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) {
15871             //el = Roo.get(document.body);
15872             this.parent = { el : true };
15873         }
15874             
15875             
15876         
15877         if (!this.parent) {
15878             
15879             Roo.log("no parent - creating one");
15880             
15881             el = el ? Roo.get(el) : false;      
15882             
15883             // it's a top level one..
15884             this.parent =  {
15885                 el : new Roo.BorderLayout(el || document.body, {
15886                 
15887                      center: {
15888                          titlebar: false,
15889                          autoScroll:false,
15890                          closeOnTab: true,
15891                          tabPosition: 'top',
15892                           //resizeTabs: true,
15893                          alwaysShowTabs: el && hp? false :  true,
15894                          hideTabs: el || !hp ? true :  false,
15895                          minTabWidth: 140
15896                      }
15897                  })
15898             }
15899         }
15900         
15901                 if (!this.parent.el) {
15902                         // probably an old style ctor, which has been disabled.
15903                         return;
15904                         
15905                 }
15906                 // The 'tree' method is  '_tree now' 
15907             
15908         tree.region = tree.region || this.region;
15909         
15910         if (this.parent.el === true) {
15911             // bootstrap... - body..
15912             this.parent.el = Roo.factory(tree);
15913         }
15914         
15915         this.el = this.parent.el.addxtype(tree);
15916         this.fireEvent('built', this);
15917         
15918         this.panel = this.el;
15919         this.layout = this.panel.layout;
15920                 this.parentLayout = this.parent.layout  || false;  
15921          
15922     }
15923     
15924 });
15925
15926 Roo.apply(Roo.XComponent, {
15927     /**
15928      * @property  hideProgress
15929      * true to disable the building progress bar.. usefull on single page renders.
15930      * @type Boolean
15931      */
15932     hideProgress : false,
15933     /**
15934      * @property  buildCompleted
15935      * True when the builder has completed building the interface.
15936      * @type Boolean
15937      */
15938     buildCompleted : false,
15939      
15940     /**
15941      * @property  topModule
15942      * the upper most module - uses document.element as it's constructor.
15943      * @type Object
15944      */
15945      
15946     topModule  : false,
15947       
15948     /**
15949      * @property  modules
15950      * array of modules to be created by registration system.
15951      * @type {Array} of Roo.XComponent
15952      */
15953     
15954     modules : [],
15955     /**
15956      * @property  elmodules
15957      * array of modules to be created by which use #ID 
15958      * @type {Array} of Roo.XComponent
15959      */
15960      
15961     elmodules : [],
15962
15963      /**
15964      * @property  build_from_html
15965      * Build elements from html - used by bootstrap HTML stuff 
15966      *    - this is cleared after build is completed
15967      * @type {boolean} true  (default false)
15968      */
15969      
15970     build_from_html : false,
15971
15972     /**
15973      * Register components to be built later.
15974      *
15975      * This solves the following issues
15976      * - Building is not done on page load, but after an authentication process has occured.
15977      * - Interface elements are registered on page load
15978      * - Parent Interface elements may not be loaded before child, so this handles that..
15979      * 
15980      *
15981      * example:
15982      * 
15983      * MyApp.register({
15984           order : '000001',
15985           module : 'Pman.Tab.projectMgr',
15986           region : 'center',
15987           parent : 'Pman.layout',
15988           disabled : false,  // or use a function..
15989         })
15990      
15991      * * @param {Object} details about module
15992      */
15993     register : function(obj) {
15994                 
15995         Roo.XComponent.event.fireEvent('register', obj);
15996         switch(typeof(obj.disabled) ) {
15997                 
15998             case 'undefined':
15999                 break;
16000             
16001             case 'function':
16002                 if ( obj.disabled() ) {
16003                         return;
16004                 }
16005                 break;
16006             
16007             default:
16008                 if (obj.disabled) {
16009                         return;
16010                 }
16011                 break;
16012         }
16013                 
16014         this.modules.push(obj);
16015          
16016     },
16017     /**
16018      * convert a string to an object..
16019      * eg. 'AAA.BBB' -> finds AAA.BBB
16020
16021      */
16022     
16023     toObject : function(str)
16024     {
16025         if (!str || typeof(str) == 'object') {
16026             return str;
16027         }
16028         if (str.substring(0,1) == '#') {
16029             return str;
16030         }
16031
16032         var ar = str.split('.');
16033         var rt, o;
16034         rt = ar.shift();
16035             /** eval:var:o */
16036         try {
16037             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16038         } catch (e) {
16039             throw "Module not found : " + str;
16040         }
16041         
16042         if (o === false) {
16043             throw "Module not found : " + str;
16044         }
16045         Roo.each(ar, function(e) {
16046             if (typeof(o[e]) == 'undefined') {
16047                 throw "Module not found : " + str;
16048             }
16049             o = o[e];
16050         });
16051         
16052         return o;
16053         
16054     },
16055     
16056     
16057     /**
16058      * move modules into their correct place in the tree..
16059      * 
16060      */
16061     preBuild : function ()
16062     {
16063         var _t = this;
16064         Roo.each(this.modules , function (obj)
16065         {
16066             Roo.XComponent.event.fireEvent('beforebuild', obj);
16067             
16068             var opar = obj.parent;
16069             try { 
16070                 obj.parent = this.toObject(opar);
16071             } catch(e) {
16072                 Roo.log("parent:toObject failed: " + e.toString());
16073                 return;
16074             }
16075             
16076             if (!obj.parent) {
16077                 Roo.debug && Roo.log("GOT top level module");
16078                 Roo.debug && Roo.log(obj);
16079                 obj.modules = new Roo.util.MixedCollection(false, 
16080                     function(o) { return o.order + '' }
16081                 );
16082                 this.topModule = obj;
16083                 return;
16084             }
16085                         // parent is a string (usually a dom element name..)
16086             if (typeof(obj.parent) == 'string') {
16087                 this.elmodules.push(obj);
16088                 return;
16089             }
16090             if (obj.parent.constructor != Roo.XComponent) {
16091                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16092             }
16093             if (!obj.parent.modules) {
16094                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16095                     function(o) { return o.order + '' }
16096                 );
16097             }
16098             if (obj.parent.disabled) {
16099                 obj.disabled = true;
16100             }
16101             obj.parent.modules.add(obj);
16102         }, this);
16103     },
16104     
16105      /**
16106      * make a list of modules to build.
16107      * @return {Array} list of modules. 
16108      */ 
16109     
16110     buildOrder : function()
16111     {
16112         var _this = this;
16113         var cmp = function(a,b) {   
16114             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16115         };
16116         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16117             throw "No top level modules to build";
16118         }
16119         
16120         // make a flat list in order of modules to build.
16121         var mods = this.topModule ? [ this.topModule ] : [];
16122                 
16123         
16124         // elmodules (is a list of DOM based modules )
16125         Roo.each(this.elmodules, function(e) {
16126             mods.push(e);
16127             if (!this.topModule &&
16128                 typeof(e.parent) == 'string' &&
16129                 e.parent.substring(0,1) == '#' &&
16130                 Roo.get(e.parent.substr(1))
16131                ) {
16132                 
16133                 _this.topModule = e;
16134             }
16135             
16136         });
16137
16138         
16139         // add modules to their parents..
16140         var addMod = function(m) {
16141             Roo.debug && Roo.log("build Order: add: " + m.name);
16142                 
16143             mods.push(m);
16144             if (m.modules && !m.disabled) {
16145                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16146                 m.modules.keySort('ASC',  cmp );
16147                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16148     
16149                 m.modules.each(addMod);
16150             } else {
16151                 Roo.debug && Roo.log("build Order: no child modules");
16152             }
16153             // not sure if this is used any more..
16154             if (m.finalize) {
16155                 m.finalize.name = m.name + " (clean up) ";
16156                 mods.push(m.finalize);
16157             }
16158             
16159         }
16160         if (this.topModule && this.topModule.modules) { 
16161             this.topModule.modules.keySort('ASC',  cmp );
16162             this.topModule.modules.each(addMod);
16163         } 
16164         return mods;
16165     },
16166     
16167      /**
16168      * Build the registered modules.
16169      * @param {Object} parent element.
16170      * @param {Function} optional method to call after module has been added.
16171      * 
16172      */ 
16173    
16174     build : function(opts) 
16175     {
16176         
16177         if (typeof(opts) != 'undefined') {
16178             Roo.apply(this,opts);
16179         }
16180         
16181         this.preBuild();
16182         var mods = this.buildOrder();
16183       
16184         //this.allmods = mods;
16185         //Roo.debug && Roo.log(mods);
16186         //return;
16187         if (!mods.length) { // should not happen
16188             throw "NO modules!!!";
16189         }
16190         
16191         
16192         var msg = "Building Interface...";
16193         // flash it up as modal - so we store the mask!?
16194         if (!this.hideProgress && Roo.MessageBox) {
16195             Roo.MessageBox.show({ title: 'loading' });
16196             Roo.MessageBox.show({
16197                title: "Please wait...",
16198                msg: msg,
16199                width:450,
16200                progress:true,
16201                closable:false,
16202                modal: false
16203               
16204             });
16205         }
16206         var total = mods.length;
16207         
16208         var _this = this;
16209         var progressRun = function() {
16210             if (!mods.length) {
16211                 Roo.debug && Roo.log('hide?');
16212                 if (!this.hideProgress && Roo.MessageBox) {
16213                     Roo.MessageBox.hide();
16214                 }
16215                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16216                 
16217                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16218                 
16219                 // THE END...
16220                 return false;   
16221             }
16222             
16223             var m = mods.shift();
16224             
16225             
16226             Roo.debug && Roo.log(m);
16227             // not sure if this is supported any more.. - modules that are are just function
16228             if (typeof(m) == 'function') { 
16229                 m.call(this);
16230                 return progressRun.defer(10, _this);
16231             } 
16232             
16233             
16234             msg = "Building Interface " + (total  - mods.length) + 
16235                     " of " + total + 
16236                     (m.name ? (' - ' + m.name) : '');
16237                         Roo.debug && Roo.log(msg);
16238             if (!this.hideProgress &&  Roo.MessageBox) { 
16239                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16240             }
16241             
16242          
16243             // is the module disabled?
16244             var disabled = (typeof(m.disabled) == 'function') ?
16245                 m.disabled.call(m.module.disabled) : m.disabled;    
16246             
16247             
16248             if (disabled) {
16249                 return progressRun(); // we do not update the display!
16250             }
16251             
16252             // now build 
16253             
16254                         
16255                         
16256             m.render();
16257             // it's 10 on top level, and 1 on others??? why...
16258             return progressRun.defer(10, _this);
16259              
16260         }
16261         progressRun.defer(1, _this);
16262      
16263         
16264         
16265     },
16266         
16267         
16268         /**
16269          * Event Object.
16270          *
16271          *
16272          */
16273         event: false, 
16274     /**
16275          * wrapper for event.on - aliased later..  
16276          * Typically use to register a event handler for register:
16277          *
16278          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16279          *
16280          */
16281     on : false
16282    
16283     
16284     
16285 });
16286
16287 Roo.XComponent.event = new Roo.util.Observable({
16288                 events : { 
16289                         /**
16290                          * @event register
16291                          * Fires when an Component is registered,
16292                          * set the disable property on the Component to stop registration.
16293                          * @param {Roo.XComponent} c the component being registerd.
16294                          * 
16295                          */
16296                         'register' : true,
16297             /**
16298                          * @event beforebuild
16299                          * Fires before each Component is built
16300                          * can be used to apply permissions.
16301                          * @param {Roo.XComponent} c the component being registerd.
16302                          * 
16303                          */
16304                         'beforebuild' : true,
16305                         /**
16306                          * @event buildcomplete
16307                          * Fires on the top level element when all elements have been built
16308                          * @param {Roo.XComponent} the top level component.
16309                          */
16310                         'buildcomplete' : true
16311                         
16312                 }
16313 });
16314
16315 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16316  /*
16317  * Based on:
16318  * Ext JS Library 1.1.1
16319  * Copyright(c) 2006-2007, Ext JS, LLC.
16320  *
16321  * Originally Released Under LGPL - original licence link has changed is not relivant.
16322  *
16323  * Fork - LGPL
16324  * <script type="text/javascript">
16325  */
16326
16327
16328
16329 /*
16330  * These classes are derivatives of the similarly named classes in the YUI Library.
16331  * The original license:
16332  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16333  * Code licensed under the BSD License:
16334  * http://developer.yahoo.net/yui/license.txt
16335  */
16336
16337 (function() {
16338
16339 var Event=Roo.EventManager;
16340 var Dom=Roo.lib.Dom;
16341
16342 /**
16343  * @class Roo.dd.DragDrop
16344  * @extends Roo.util.Observable
16345  * Defines the interface and base operation of items that that can be
16346  * dragged or can be drop targets.  It was designed to be extended, overriding
16347  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16348  * Up to three html elements can be associated with a DragDrop instance:
16349  * <ul>
16350  * <li>linked element: the element that is passed into the constructor.
16351  * This is the element which defines the boundaries for interaction with
16352  * other DragDrop objects.</li>
16353  * <li>handle element(s): The drag operation only occurs if the element that
16354  * was clicked matches a handle element.  By default this is the linked
16355  * element, but there are times that you will want only a portion of the
16356  * linked element to initiate the drag operation, and the setHandleElId()
16357  * method provides a way to define this.</li>
16358  * <li>drag element: this represents the element that would be moved along
16359  * with the cursor during a drag operation.  By default, this is the linked
16360  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16361  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16362  * </li>
16363  * </ul>
16364  * This class should not be instantiated until the onload event to ensure that
16365  * the associated elements are available.
16366  * The following would define a DragDrop obj that would interact with any
16367  * other DragDrop obj in the "group1" group:
16368  * <pre>
16369  *  dd = new Roo.dd.DragDrop("div1", "group1");
16370  * </pre>
16371  * Since none of the event handlers have been implemented, nothing would
16372  * actually happen if you were to run the code above.  Normally you would
16373  * override this class or one of the default implementations, but you can
16374  * also override the methods you want on an instance of the class...
16375  * <pre>
16376  *  dd.onDragDrop = function(e, id) {
16377  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16378  *  }
16379  * </pre>
16380  * @constructor
16381  * @param {String} id of the element that is linked to this instance
16382  * @param {String} sGroup the group of related DragDrop objects
16383  * @param {object} config an object containing configurable attributes
16384  *                Valid properties for DragDrop:
16385  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16386  */
16387 Roo.dd.DragDrop = function(id, sGroup, config) {
16388     if (id) {
16389         this.init(id, sGroup, config);
16390     }
16391     
16392 };
16393
16394 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16395
16396     /**
16397      * The id of the element associated with this object.  This is what we
16398      * refer to as the "linked element" because the size and position of
16399      * this element is used to determine when the drag and drop objects have
16400      * interacted.
16401      * @property id
16402      * @type String
16403      */
16404     id: null,
16405
16406     /**
16407      * Configuration attributes passed into the constructor
16408      * @property config
16409      * @type object
16410      */
16411     config: null,
16412
16413     /**
16414      * The id of the element that will be dragged.  By default this is same
16415      * as the linked element , but could be changed to another element. Ex:
16416      * Roo.dd.DDProxy
16417      * @property dragElId
16418      * @type String
16419      * @private
16420      */
16421     dragElId: null,
16422
16423     /**
16424      * the id of the element that initiates the drag operation.  By default
16425      * this is the linked element, but could be changed to be a child of this
16426      * element.  This lets us do things like only starting the drag when the
16427      * header element within the linked html element is clicked.
16428      * @property handleElId
16429      * @type String
16430      * @private
16431      */
16432     handleElId: null,
16433
16434     /**
16435      * An associative array of HTML tags that will be ignored if clicked.
16436      * @property invalidHandleTypes
16437      * @type {string: string}
16438      */
16439     invalidHandleTypes: null,
16440
16441     /**
16442      * An associative array of ids for elements that will be ignored if clicked
16443      * @property invalidHandleIds
16444      * @type {string: string}
16445      */
16446     invalidHandleIds: null,
16447
16448     /**
16449      * An indexted array of css class names for elements that will be ignored
16450      * if clicked.
16451      * @property invalidHandleClasses
16452      * @type string[]
16453      */
16454     invalidHandleClasses: null,
16455
16456     /**
16457      * The linked element's absolute X position at the time the drag was
16458      * started
16459      * @property startPageX
16460      * @type int
16461      * @private
16462      */
16463     startPageX: 0,
16464
16465     /**
16466      * The linked element's absolute X position at the time the drag was
16467      * started
16468      * @property startPageY
16469      * @type int
16470      * @private
16471      */
16472     startPageY: 0,
16473
16474     /**
16475      * The group defines a logical collection of DragDrop objects that are
16476      * related.  Instances only get events when interacting with other
16477      * DragDrop object in the same group.  This lets us define multiple
16478      * groups using a single DragDrop subclass if we want.
16479      * @property groups
16480      * @type {string: string}
16481      */
16482     groups: null,
16483
16484     /**
16485      * Individual drag/drop instances can be locked.  This will prevent
16486      * onmousedown start drag.
16487      * @property locked
16488      * @type boolean
16489      * @private
16490      */
16491     locked: false,
16492
16493     /**
16494      * Lock this instance
16495      * @method lock
16496      */
16497     lock: function() { this.locked = true; },
16498
16499     /**
16500      * Unlock this instace
16501      * @method unlock
16502      */
16503     unlock: function() { this.locked = false; },
16504
16505     /**
16506      * By default, all insances can be a drop target.  This can be disabled by
16507      * setting isTarget to false.
16508      * @method isTarget
16509      * @type boolean
16510      */
16511     isTarget: true,
16512
16513     /**
16514      * The padding configured for this drag and drop object for calculating
16515      * the drop zone intersection with this object.
16516      * @method padding
16517      * @type int[]
16518      */
16519     padding: null,
16520
16521     /**
16522      * Cached reference to the linked element
16523      * @property _domRef
16524      * @private
16525      */
16526     _domRef: null,
16527
16528     /**
16529      * Internal typeof flag
16530      * @property __ygDragDrop
16531      * @private
16532      */
16533     __ygDragDrop: true,
16534
16535     /**
16536      * Set to true when horizontal contraints are applied
16537      * @property constrainX
16538      * @type boolean
16539      * @private
16540      */
16541     constrainX: false,
16542
16543     /**
16544      * Set to true when vertical contraints are applied
16545      * @property constrainY
16546      * @type boolean
16547      * @private
16548      */
16549     constrainY: false,
16550
16551     /**
16552      * The left constraint
16553      * @property minX
16554      * @type int
16555      * @private
16556      */
16557     minX: 0,
16558
16559     /**
16560      * The right constraint
16561      * @property maxX
16562      * @type int
16563      * @private
16564      */
16565     maxX: 0,
16566
16567     /**
16568      * The up constraint
16569      * @property minY
16570      * @type int
16571      * @type int
16572      * @private
16573      */
16574     minY: 0,
16575
16576     /**
16577      * The down constraint
16578      * @property maxY
16579      * @type int
16580      * @private
16581      */
16582     maxY: 0,
16583
16584     /**
16585      * Maintain offsets when we resetconstraints.  Set to true when you want
16586      * the position of the element relative to its parent to stay the same
16587      * when the page changes
16588      *
16589      * @property maintainOffset
16590      * @type boolean
16591      */
16592     maintainOffset: false,
16593
16594     /**
16595      * Array of pixel locations the element will snap to if we specified a
16596      * horizontal graduation/interval.  This array is generated automatically
16597      * when you define a tick interval.
16598      * @property xTicks
16599      * @type int[]
16600      */
16601     xTicks: null,
16602
16603     /**
16604      * Array of pixel locations the element will snap to if we specified a
16605      * vertical graduation/interval.  This array is generated automatically
16606      * when you define a tick interval.
16607      * @property yTicks
16608      * @type int[]
16609      */
16610     yTicks: null,
16611
16612     /**
16613      * By default the drag and drop instance will only respond to the primary
16614      * button click (left button for a right-handed mouse).  Set to true to
16615      * allow drag and drop to start with any mouse click that is propogated
16616      * by the browser
16617      * @property primaryButtonOnly
16618      * @type boolean
16619      */
16620     primaryButtonOnly: true,
16621
16622     /**
16623      * The availabe property is false until the linked dom element is accessible.
16624      * @property available
16625      * @type boolean
16626      */
16627     available: false,
16628
16629     /**
16630      * By default, drags can only be initiated if the mousedown occurs in the
16631      * region the linked element is.  This is done in part to work around a
16632      * bug in some browsers that mis-report the mousedown if the previous
16633      * mouseup happened outside of the window.  This property is set to true
16634      * if outer handles are defined.
16635      *
16636      * @property hasOuterHandles
16637      * @type boolean
16638      * @default false
16639      */
16640     hasOuterHandles: false,
16641
16642     /**
16643      * Code that executes immediately before the startDrag event
16644      * @method b4StartDrag
16645      * @private
16646      */
16647     b4StartDrag: function(x, y) { },
16648
16649     /**
16650      * Abstract method called after a drag/drop object is clicked
16651      * and the drag or mousedown time thresholds have beeen met.
16652      * @method startDrag
16653      * @param {int} X click location
16654      * @param {int} Y click location
16655      */
16656     startDrag: function(x, y) { /* override this */ },
16657
16658     /**
16659      * Code that executes immediately before the onDrag event
16660      * @method b4Drag
16661      * @private
16662      */
16663     b4Drag: function(e) { },
16664
16665     /**
16666      * Abstract method called during the onMouseMove event while dragging an
16667      * object.
16668      * @method onDrag
16669      * @param {Event} e the mousemove event
16670      */
16671     onDrag: function(e) { /* override this */ },
16672
16673     /**
16674      * Abstract method called when this element fist begins hovering over
16675      * another DragDrop obj
16676      * @method onDragEnter
16677      * @param {Event} e the mousemove event
16678      * @param {String|DragDrop[]} id In POINT mode, the element
16679      * id this is hovering over.  In INTERSECT mode, an array of one or more
16680      * dragdrop items being hovered over.
16681      */
16682     onDragEnter: function(e, id) { /* override this */ },
16683
16684     /**
16685      * Code that executes immediately before the onDragOver event
16686      * @method b4DragOver
16687      * @private
16688      */
16689     b4DragOver: function(e) { },
16690
16691     /**
16692      * Abstract method called when this element is hovering over another
16693      * DragDrop obj
16694      * @method onDragOver
16695      * @param {Event} e the mousemove event
16696      * @param {String|DragDrop[]} id In POINT mode, the element
16697      * id this is hovering over.  In INTERSECT mode, an array of dd items
16698      * being hovered over.
16699      */
16700     onDragOver: function(e, id) { /* override this */ },
16701
16702     /**
16703      * Code that executes immediately before the onDragOut event
16704      * @method b4DragOut
16705      * @private
16706      */
16707     b4DragOut: function(e) { },
16708
16709     /**
16710      * Abstract method called when we are no longer hovering over an element
16711      * @method onDragOut
16712      * @param {Event} e the mousemove event
16713      * @param {String|DragDrop[]} id In POINT mode, the element
16714      * id this was hovering over.  In INTERSECT mode, an array of dd items
16715      * that the mouse is no longer over.
16716      */
16717     onDragOut: function(e, id) { /* override this */ },
16718
16719     /**
16720      * Code that executes immediately before the onDragDrop event
16721      * @method b4DragDrop
16722      * @private
16723      */
16724     b4DragDrop: function(e) { },
16725
16726     /**
16727      * Abstract method called when this item is dropped on another DragDrop
16728      * obj
16729      * @method onDragDrop
16730      * @param {Event} e the mouseup event
16731      * @param {String|DragDrop[]} id In POINT mode, the element
16732      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16733      * was dropped on.
16734      */
16735     onDragDrop: function(e, id) { /* override this */ },
16736
16737     /**
16738      * Abstract method called when this item is dropped on an area with no
16739      * drop target
16740      * @method onInvalidDrop
16741      * @param {Event} e the mouseup event
16742      */
16743     onInvalidDrop: function(e) { /* override this */ },
16744
16745     /**
16746      * Code that executes immediately before the endDrag event
16747      * @method b4EndDrag
16748      * @private
16749      */
16750     b4EndDrag: function(e) { },
16751
16752     /**
16753      * Fired when we are done dragging the object
16754      * @method endDrag
16755      * @param {Event} e the mouseup event
16756      */
16757     endDrag: function(e) { /* override this */ },
16758
16759     /**
16760      * Code executed immediately before the onMouseDown event
16761      * @method b4MouseDown
16762      * @param {Event} e the mousedown event
16763      * @private
16764      */
16765     b4MouseDown: function(e) {  },
16766
16767     /**
16768      * Event handler that fires when a drag/drop obj gets a mousedown
16769      * @method onMouseDown
16770      * @param {Event} e the mousedown event
16771      */
16772     onMouseDown: function(e) { /* override this */ },
16773
16774     /**
16775      * Event handler that fires when a drag/drop obj gets a mouseup
16776      * @method onMouseUp
16777      * @param {Event} e the mouseup event
16778      */
16779     onMouseUp: function(e) { /* override this */ },
16780
16781     /**
16782      * Override the onAvailable method to do what is needed after the initial
16783      * position was determined.
16784      * @method onAvailable
16785      */
16786     onAvailable: function () {
16787     },
16788
16789     /*
16790      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16791      * @type Object
16792      */
16793     defaultPadding : {left:0, right:0, top:0, bottom:0},
16794
16795     /*
16796      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16797  *
16798  * Usage:
16799  <pre><code>
16800  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16801                 { dragElId: "existingProxyDiv" });
16802  dd.startDrag = function(){
16803      this.constrainTo("parent-id");
16804  };
16805  </code></pre>
16806  * Or you can initalize it using the {@link Roo.Element} object:
16807  <pre><code>
16808  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16809      startDrag : function(){
16810          this.constrainTo("parent-id");
16811      }
16812  });
16813  </code></pre>
16814      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16815      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16816      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16817      * an object containing the sides to pad. For example: {right:10, bottom:10}
16818      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16819      */
16820     constrainTo : function(constrainTo, pad, inContent){
16821         if(typeof pad == "number"){
16822             pad = {left: pad, right:pad, top:pad, bottom:pad};
16823         }
16824         pad = pad || this.defaultPadding;
16825         var b = Roo.get(this.getEl()).getBox();
16826         var ce = Roo.get(constrainTo);
16827         var s = ce.getScroll();
16828         var c, cd = ce.dom;
16829         if(cd == document.body){
16830             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16831         }else{
16832             xy = ce.getXY();
16833             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16834         }
16835
16836
16837         var topSpace = b.y - c.y;
16838         var leftSpace = b.x - c.x;
16839
16840         this.resetConstraints();
16841         this.setXConstraint(leftSpace - (pad.left||0), // left
16842                 c.width - leftSpace - b.width - (pad.right||0) //right
16843         );
16844         this.setYConstraint(topSpace - (pad.top||0), //top
16845                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16846         );
16847     },
16848
16849     /**
16850      * Returns a reference to the linked element
16851      * @method getEl
16852      * @return {HTMLElement} the html element
16853      */
16854     getEl: function() {
16855         if (!this._domRef) {
16856             this._domRef = Roo.getDom(this.id);
16857         }
16858
16859         return this._domRef;
16860     },
16861
16862     /**
16863      * Returns a reference to the actual element to drag.  By default this is
16864      * the same as the html element, but it can be assigned to another
16865      * element. An example of this can be found in Roo.dd.DDProxy
16866      * @method getDragEl
16867      * @return {HTMLElement} the html element
16868      */
16869     getDragEl: function() {
16870         return Roo.getDom(this.dragElId);
16871     },
16872
16873     /**
16874      * Sets up the DragDrop object.  Must be called in the constructor of any
16875      * Roo.dd.DragDrop subclass
16876      * @method init
16877      * @param id the id of the linked element
16878      * @param {String} sGroup the group of related items
16879      * @param {object} config configuration attributes
16880      */
16881     init: function(id, sGroup, config) {
16882         this.initTarget(id, sGroup, config);
16883         if (!Roo.isTouch) {
16884             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16885         }
16886         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16887         // Event.on(this.id, "selectstart", Event.preventDefault);
16888     },
16889
16890     /**
16891      * Initializes Targeting functionality only... the object does not
16892      * get a mousedown handler.
16893      * @method initTarget
16894      * @param id the id of the linked element
16895      * @param {String} sGroup the group of related items
16896      * @param {object} config configuration attributes
16897      */
16898     initTarget: function(id, sGroup, config) {
16899
16900         // configuration attributes
16901         this.config = config || {};
16902
16903         // create a local reference to the drag and drop manager
16904         this.DDM = Roo.dd.DDM;
16905         // initialize the groups array
16906         this.groups = {};
16907
16908         // assume that we have an element reference instead of an id if the
16909         // parameter is not a string
16910         if (typeof id !== "string") {
16911             id = Roo.id(id);
16912         }
16913
16914         // set the id
16915         this.id = id;
16916
16917         // add to an interaction group
16918         this.addToGroup((sGroup) ? sGroup : "default");
16919
16920         // We don't want to register this as the handle with the manager
16921         // so we just set the id rather than calling the setter.
16922         this.handleElId = id;
16923
16924         // the linked element is the element that gets dragged by default
16925         this.setDragElId(id);
16926
16927         // by default, clicked anchors will not start drag operations.
16928         this.invalidHandleTypes = { A: "A" };
16929         this.invalidHandleIds = {};
16930         this.invalidHandleClasses = [];
16931
16932         this.applyConfig();
16933
16934         this.handleOnAvailable();
16935     },
16936
16937     /**
16938      * Applies the configuration parameters that were passed into the constructor.
16939      * This is supposed to happen at each level through the inheritance chain.  So
16940      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16941      * DragDrop in order to get all of the parameters that are available in
16942      * each object.
16943      * @method applyConfig
16944      */
16945     applyConfig: function() {
16946
16947         // configurable properties:
16948         //    padding, isTarget, maintainOffset, primaryButtonOnly
16949         this.padding           = this.config.padding || [0, 0, 0, 0];
16950         this.isTarget          = (this.config.isTarget !== false);
16951         this.maintainOffset    = (this.config.maintainOffset);
16952         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16953
16954     },
16955
16956     /**
16957      * Executed when the linked element is available
16958      * @method handleOnAvailable
16959      * @private
16960      */
16961     handleOnAvailable: function() {
16962         this.available = true;
16963         this.resetConstraints();
16964         this.onAvailable();
16965     },
16966
16967      /**
16968      * Configures the padding for the target zone in px.  Effectively expands
16969      * (or reduces) the virtual object size for targeting calculations.
16970      * Supports css-style shorthand; if only one parameter is passed, all sides
16971      * will have that padding, and if only two are passed, the top and bottom
16972      * will have the first param, the left and right the second.
16973      * @method setPadding
16974      * @param {int} iTop    Top pad
16975      * @param {int} iRight  Right pad
16976      * @param {int} iBot    Bot pad
16977      * @param {int} iLeft   Left pad
16978      */
16979     setPadding: function(iTop, iRight, iBot, iLeft) {
16980         // this.padding = [iLeft, iRight, iTop, iBot];
16981         if (!iRight && 0 !== iRight) {
16982             this.padding = [iTop, iTop, iTop, iTop];
16983         } else if (!iBot && 0 !== iBot) {
16984             this.padding = [iTop, iRight, iTop, iRight];
16985         } else {
16986             this.padding = [iTop, iRight, iBot, iLeft];
16987         }
16988     },
16989
16990     /**
16991      * Stores the initial placement of the linked element.
16992      * @method setInitialPosition
16993      * @param {int} diffX   the X offset, default 0
16994      * @param {int} diffY   the Y offset, default 0
16995      */
16996     setInitPosition: function(diffX, diffY) {
16997         var el = this.getEl();
16998
16999         if (!this.DDM.verifyEl(el)) {
17000             return;
17001         }
17002
17003         var dx = diffX || 0;
17004         var dy = diffY || 0;
17005
17006         var p = Dom.getXY( el );
17007
17008         this.initPageX = p[0] - dx;
17009         this.initPageY = p[1] - dy;
17010
17011         this.lastPageX = p[0];
17012         this.lastPageY = p[1];
17013
17014
17015         this.setStartPosition(p);
17016     },
17017
17018     /**
17019      * Sets the start position of the element.  This is set when the obj
17020      * is initialized, the reset when a drag is started.
17021      * @method setStartPosition
17022      * @param pos current position (from previous lookup)
17023      * @private
17024      */
17025     setStartPosition: function(pos) {
17026         var p = pos || Dom.getXY( this.getEl() );
17027         this.deltaSetXY = null;
17028
17029         this.startPageX = p[0];
17030         this.startPageY = p[1];
17031     },
17032
17033     /**
17034      * Add this instance to a group of related drag/drop objects.  All
17035      * instances belong to at least one group, and can belong to as many
17036      * groups as needed.
17037      * @method addToGroup
17038      * @param sGroup {string} the name of the group
17039      */
17040     addToGroup: function(sGroup) {
17041         this.groups[sGroup] = true;
17042         this.DDM.regDragDrop(this, sGroup);
17043     },
17044
17045     /**
17046      * Remove's this instance from the supplied interaction group
17047      * @method removeFromGroup
17048      * @param {string}  sGroup  The group to drop
17049      */
17050     removeFromGroup: function(sGroup) {
17051         if (this.groups[sGroup]) {
17052             delete this.groups[sGroup];
17053         }
17054
17055         this.DDM.removeDDFromGroup(this, sGroup);
17056     },
17057
17058     /**
17059      * Allows you to specify that an element other than the linked element
17060      * will be moved with the cursor during a drag
17061      * @method setDragElId
17062      * @param id {string} the id of the element that will be used to initiate the drag
17063      */
17064     setDragElId: function(id) {
17065         this.dragElId = id;
17066     },
17067
17068     /**
17069      * Allows you to specify a child of the linked element that should be
17070      * used to initiate the drag operation.  An example of this would be if
17071      * you have a content div with text and links.  Clicking anywhere in the
17072      * content area would normally start the drag operation.  Use this method
17073      * to specify that an element inside of the content div is the element
17074      * that starts the drag operation.
17075      * @method setHandleElId
17076      * @param id {string} the id of the element that will be used to
17077      * initiate the drag.
17078      */
17079     setHandleElId: function(id) {
17080         if (typeof id !== "string") {
17081             id = Roo.id(id);
17082         }
17083         this.handleElId = id;
17084         this.DDM.regHandle(this.id, id);
17085     },
17086
17087     /**
17088      * Allows you to set an element outside of the linked element as a drag
17089      * handle
17090      * @method setOuterHandleElId
17091      * @param id the id of the element that will be used to initiate the drag
17092      */
17093     setOuterHandleElId: function(id) {
17094         if (typeof id !== "string") {
17095             id = Roo.id(id);
17096         }
17097         Event.on(id, "mousedown",
17098                 this.handleMouseDown, this);
17099         this.setHandleElId(id);
17100
17101         this.hasOuterHandles = true;
17102     },
17103
17104     /**
17105      * Remove all drag and drop hooks for this element
17106      * @method unreg
17107      */
17108     unreg: function() {
17109         Event.un(this.id, "mousedown",
17110                 this.handleMouseDown);
17111         Event.un(this.id, "touchstart",
17112                 this.handleMouseDown);
17113         this._domRef = null;
17114         this.DDM._remove(this);
17115     },
17116
17117     destroy : function(){
17118         this.unreg();
17119     },
17120
17121     /**
17122      * Returns true if this instance is locked, or the drag drop mgr is locked
17123      * (meaning that all drag/drop is disabled on the page.)
17124      * @method isLocked
17125      * @return {boolean} true if this obj or all drag/drop is locked, else
17126      * false
17127      */
17128     isLocked: function() {
17129         return (this.DDM.isLocked() || this.locked);
17130     },
17131
17132     /**
17133      * Fired when this object is clicked
17134      * @method handleMouseDown
17135      * @param {Event} e
17136      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17137      * @private
17138      */
17139     handleMouseDown: function(e, oDD){
17140      
17141         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17142             //Roo.log('not touch/ button !=0');
17143             return;
17144         }
17145         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17146             return; // double touch..
17147         }
17148         
17149
17150         if (this.isLocked()) {
17151             //Roo.log('locked');
17152             return;
17153         }
17154
17155         this.DDM.refreshCache(this.groups);
17156 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17157         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17158         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17159             //Roo.log('no outer handes or not over target');
17160                 // do nothing.
17161         } else {
17162 //            Roo.log('check validator');
17163             if (this.clickValidator(e)) {
17164 //                Roo.log('validate success');
17165                 // set the initial element position
17166                 this.setStartPosition();
17167
17168
17169                 this.b4MouseDown(e);
17170                 this.onMouseDown(e);
17171
17172                 this.DDM.handleMouseDown(e, this);
17173
17174                 this.DDM.stopEvent(e);
17175             } else {
17176
17177
17178             }
17179         }
17180     },
17181
17182     clickValidator: function(e) {
17183         var target = e.getTarget();
17184         return ( this.isValidHandleChild(target) &&
17185                     (this.id == this.handleElId ||
17186                         this.DDM.handleWasClicked(target, this.id)) );
17187     },
17188
17189     /**
17190      * Allows you to specify a tag name that should not start a drag operation
17191      * when clicked.  This is designed to facilitate embedding links within a
17192      * drag handle that do something other than start the drag.
17193      * @method addInvalidHandleType
17194      * @param {string} tagName the type of element to exclude
17195      */
17196     addInvalidHandleType: function(tagName) {
17197         var type = tagName.toUpperCase();
17198         this.invalidHandleTypes[type] = type;
17199     },
17200
17201     /**
17202      * Lets you to specify an element id for a child of a drag handle
17203      * that should not initiate a drag
17204      * @method addInvalidHandleId
17205      * @param {string} id the element id of the element you wish to ignore
17206      */
17207     addInvalidHandleId: function(id) {
17208         if (typeof id !== "string") {
17209             id = Roo.id(id);
17210         }
17211         this.invalidHandleIds[id] = id;
17212     },
17213
17214     /**
17215      * Lets you specify a css class of elements that will not initiate a drag
17216      * @method addInvalidHandleClass
17217      * @param {string} cssClass the class of the elements you wish to ignore
17218      */
17219     addInvalidHandleClass: function(cssClass) {
17220         this.invalidHandleClasses.push(cssClass);
17221     },
17222
17223     /**
17224      * Unsets an excluded tag name set by addInvalidHandleType
17225      * @method removeInvalidHandleType
17226      * @param {string} tagName the type of element to unexclude
17227      */
17228     removeInvalidHandleType: function(tagName) {
17229         var type = tagName.toUpperCase();
17230         // this.invalidHandleTypes[type] = null;
17231         delete this.invalidHandleTypes[type];
17232     },
17233
17234     /**
17235      * Unsets an invalid handle id
17236      * @method removeInvalidHandleId
17237      * @param {string} id the id of the element to re-enable
17238      */
17239     removeInvalidHandleId: function(id) {
17240         if (typeof id !== "string") {
17241             id = Roo.id(id);
17242         }
17243         delete this.invalidHandleIds[id];
17244     },
17245
17246     /**
17247      * Unsets an invalid css class
17248      * @method removeInvalidHandleClass
17249      * @param {string} cssClass the class of the element(s) you wish to
17250      * re-enable
17251      */
17252     removeInvalidHandleClass: function(cssClass) {
17253         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17254             if (this.invalidHandleClasses[i] == cssClass) {
17255                 delete this.invalidHandleClasses[i];
17256             }
17257         }
17258     },
17259
17260     /**
17261      * Checks the tag exclusion list to see if this click should be ignored
17262      * @method isValidHandleChild
17263      * @param {HTMLElement} node the HTMLElement to evaluate
17264      * @return {boolean} true if this is a valid tag type, false if not
17265      */
17266     isValidHandleChild: function(node) {
17267
17268         var valid = true;
17269         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17270         var nodeName;
17271         try {
17272             nodeName = node.nodeName.toUpperCase();
17273         } catch(e) {
17274             nodeName = node.nodeName;
17275         }
17276         valid = valid && !this.invalidHandleTypes[nodeName];
17277         valid = valid && !this.invalidHandleIds[node.id];
17278
17279         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17280             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17281         }
17282
17283
17284         return valid;
17285
17286     },
17287
17288     /**
17289      * Create the array of horizontal tick marks if an interval was specified
17290      * in setXConstraint().
17291      * @method setXTicks
17292      * @private
17293      */
17294     setXTicks: function(iStartX, iTickSize) {
17295         this.xTicks = [];
17296         this.xTickSize = iTickSize;
17297
17298         var tickMap = {};
17299
17300         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17301             if (!tickMap[i]) {
17302                 this.xTicks[this.xTicks.length] = i;
17303                 tickMap[i] = true;
17304             }
17305         }
17306
17307         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17308             if (!tickMap[i]) {
17309                 this.xTicks[this.xTicks.length] = i;
17310                 tickMap[i] = true;
17311             }
17312         }
17313
17314         this.xTicks.sort(this.DDM.numericSort) ;
17315     },
17316
17317     /**
17318      * Create the array of vertical tick marks if an interval was specified in
17319      * setYConstraint().
17320      * @method setYTicks
17321      * @private
17322      */
17323     setYTicks: function(iStartY, iTickSize) {
17324         this.yTicks = [];
17325         this.yTickSize = iTickSize;
17326
17327         var tickMap = {};
17328
17329         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17330             if (!tickMap[i]) {
17331                 this.yTicks[this.yTicks.length] = i;
17332                 tickMap[i] = true;
17333             }
17334         }
17335
17336         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17337             if (!tickMap[i]) {
17338                 this.yTicks[this.yTicks.length] = i;
17339                 tickMap[i] = true;
17340             }
17341         }
17342
17343         this.yTicks.sort(this.DDM.numericSort) ;
17344     },
17345
17346     /**
17347      * By default, the element can be dragged any place on the screen.  Use
17348      * this method to limit the horizontal travel of the element.  Pass in
17349      * 0,0 for the parameters if you want to lock the drag to the y axis.
17350      * @method setXConstraint
17351      * @param {int} iLeft the number of pixels the element can move to the left
17352      * @param {int} iRight the number of pixels the element can move to the
17353      * right
17354      * @param {int} iTickSize optional parameter for specifying that the
17355      * element
17356      * should move iTickSize pixels at a time.
17357      */
17358     setXConstraint: function(iLeft, iRight, iTickSize) {
17359         this.leftConstraint = iLeft;
17360         this.rightConstraint = iRight;
17361
17362         this.minX = this.initPageX - iLeft;
17363         this.maxX = this.initPageX + iRight;
17364         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17365
17366         this.constrainX = true;
17367     },
17368
17369     /**
17370      * Clears any constraints applied to this instance.  Also clears ticks
17371      * since they can't exist independent of a constraint at this time.
17372      * @method clearConstraints
17373      */
17374     clearConstraints: function() {
17375         this.constrainX = false;
17376         this.constrainY = false;
17377         this.clearTicks();
17378     },
17379
17380     /**
17381      * Clears any tick interval defined for this instance
17382      * @method clearTicks
17383      */
17384     clearTicks: function() {
17385         this.xTicks = null;
17386         this.yTicks = null;
17387         this.xTickSize = 0;
17388         this.yTickSize = 0;
17389     },
17390
17391     /**
17392      * By default, the element can be dragged any place on the screen.  Set
17393      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17394      * parameters if you want to lock the drag to the x axis.
17395      * @method setYConstraint
17396      * @param {int} iUp the number of pixels the element can move up
17397      * @param {int} iDown the number of pixels the element can move down
17398      * @param {int} iTickSize optional parameter for specifying that the
17399      * element should move iTickSize pixels at a time.
17400      */
17401     setYConstraint: function(iUp, iDown, iTickSize) {
17402         this.topConstraint = iUp;
17403         this.bottomConstraint = iDown;
17404
17405         this.minY = this.initPageY - iUp;
17406         this.maxY = this.initPageY + iDown;
17407         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17408
17409         this.constrainY = true;
17410
17411     },
17412
17413     /**
17414      * resetConstraints must be called if you manually reposition a dd element.
17415      * @method resetConstraints
17416      * @param {boolean} maintainOffset
17417      */
17418     resetConstraints: function() {
17419
17420
17421         // Maintain offsets if necessary
17422         if (this.initPageX || this.initPageX === 0) {
17423             // figure out how much this thing has moved
17424             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17425             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17426
17427             this.setInitPosition(dx, dy);
17428
17429         // This is the first time we have detected the element's position
17430         } else {
17431             this.setInitPosition();
17432         }
17433
17434         if (this.constrainX) {
17435             this.setXConstraint( this.leftConstraint,
17436                                  this.rightConstraint,
17437                                  this.xTickSize        );
17438         }
17439
17440         if (this.constrainY) {
17441             this.setYConstraint( this.topConstraint,
17442                                  this.bottomConstraint,
17443                                  this.yTickSize         );
17444         }
17445     },
17446
17447     /**
17448      * Normally the drag element is moved pixel by pixel, but we can specify
17449      * that it move a number of pixels at a time.  This method resolves the
17450      * location when we have it set up like this.
17451      * @method getTick
17452      * @param {int} val where we want to place the object
17453      * @param {int[]} tickArray sorted array of valid points
17454      * @return {int} the closest tick
17455      * @private
17456      */
17457     getTick: function(val, tickArray) {
17458
17459         if (!tickArray) {
17460             // If tick interval is not defined, it is effectively 1 pixel,
17461             // so we return the value passed to us.
17462             return val;
17463         } else if (tickArray[0] >= val) {
17464             // The value is lower than the first tick, so we return the first
17465             // tick.
17466             return tickArray[0];
17467         } else {
17468             for (var i=0, len=tickArray.length; i<len; ++i) {
17469                 var next = i + 1;
17470                 if (tickArray[next] && tickArray[next] >= val) {
17471                     var diff1 = val - tickArray[i];
17472                     var diff2 = tickArray[next] - val;
17473                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17474                 }
17475             }
17476
17477             // The value is larger than the last tick, so we return the last
17478             // tick.
17479             return tickArray[tickArray.length - 1];
17480         }
17481     },
17482
17483     /**
17484      * toString method
17485      * @method toString
17486      * @return {string} string representation of the dd obj
17487      */
17488     toString: function() {
17489         return ("DragDrop " + this.id);
17490     }
17491
17492 });
17493
17494 })();
17495 /*
17496  * Based on:
17497  * Ext JS Library 1.1.1
17498  * Copyright(c) 2006-2007, Ext JS, LLC.
17499  *
17500  * Originally Released Under LGPL - original licence link has changed is not relivant.
17501  *
17502  * Fork - LGPL
17503  * <script type="text/javascript">
17504  */
17505
17506
17507 /**
17508  * The drag and drop utility provides a framework for building drag and drop
17509  * applications.  In addition to enabling drag and drop for specific elements,
17510  * the drag and drop elements are tracked by the manager class, and the
17511  * interactions between the various elements are tracked during the drag and
17512  * the implementing code is notified about these important moments.
17513  */
17514
17515 // Only load the library once.  Rewriting the manager class would orphan
17516 // existing drag and drop instances.
17517 if (!Roo.dd.DragDropMgr) {
17518
17519 /**
17520  * @class Roo.dd.DragDropMgr
17521  * DragDropMgr is a singleton that tracks the element interaction for
17522  * all DragDrop items in the window.  Generally, you will not call
17523  * this class directly, but it does have helper methods that could
17524  * be useful in your DragDrop implementations.
17525  * @singleton
17526  */
17527 Roo.dd.DragDropMgr = function() {
17528
17529     var Event = Roo.EventManager;
17530
17531     return {
17532
17533         /**
17534          * Two dimensional Array of registered DragDrop objects.  The first
17535          * dimension is the DragDrop item group, the second the DragDrop
17536          * object.
17537          * @property ids
17538          * @type {string: string}
17539          * @private
17540          * @static
17541          */
17542         ids: {},
17543
17544         /**
17545          * Array of element ids defined as drag handles.  Used to determine
17546          * if the element that generated the mousedown event is actually the
17547          * handle and not the html element itself.
17548          * @property handleIds
17549          * @type {string: string}
17550          * @private
17551          * @static
17552          */
17553         handleIds: {},
17554
17555         /**
17556          * the DragDrop object that is currently being dragged
17557          * @property dragCurrent
17558          * @type DragDrop
17559          * @private
17560          * @static
17561          **/
17562         dragCurrent: null,
17563
17564         /**
17565          * the DragDrop object(s) that are being hovered over
17566          * @property dragOvers
17567          * @type Array
17568          * @private
17569          * @static
17570          */
17571         dragOvers: {},
17572
17573         /**
17574          * the X distance between the cursor and the object being dragged
17575          * @property deltaX
17576          * @type int
17577          * @private
17578          * @static
17579          */
17580         deltaX: 0,
17581
17582         /**
17583          * the Y distance between the cursor and the object being dragged
17584          * @property deltaY
17585          * @type int
17586          * @private
17587          * @static
17588          */
17589         deltaY: 0,
17590
17591         /**
17592          * Flag to determine if we should prevent the default behavior of the
17593          * events we define. By default this is true, but this can be set to
17594          * false if you need the default behavior (not recommended)
17595          * @property preventDefault
17596          * @type boolean
17597          * @static
17598          */
17599         preventDefault: true,
17600
17601         /**
17602          * Flag to determine if we should stop the propagation of the events
17603          * we generate. This is true by default but you may want to set it to
17604          * false if the html element contains other features that require the
17605          * mouse click.
17606          * @property stopPropagation
17607          * @type boolean
17608          * @static
17609          */
17610         stopPropagation: true,
17611
17612         /**
17613          * Internal flag that is set to true when drag and drop has been
17614          * intialized
17615          * @property initialized
17616          * @private
17617          * @static
17618          */
17619         initalized: false,
17620
17621         /**
17622          * All drag and drop can be disabled.
17623          * @property locked
17624          * @private
17625          * @static
17626          */
17627         locked: false,
17628
17629         /**
17630          * Called the first time an element is registered.
17631          * @method init
17632          * @private
17633          * @static
17634          */
17635         init: function() {
17636             this.initialized = true;
17637         },
17638
17639         /**
17640          * In point mode, drag and drop interaction is defined by the
17641          * location of the cursor during the drag/drop
17642          * @property POINT
17643          * @type int
17644          * @static
17645          */
17646         POINT: 0,
17647
17648         /**
17649          * In intersect mode, drag and drop interactio nis defined by the
17650          * overlap of two or more drag and drop objects.
17651          * @property INTERSECT
17652          * @type int
17653          * @static
17654          */
17655         INTERSECT: 1,
17656
17657         /**
17658          * The current drag and drop mode.  Default: POINT
17659          * @property mode
17660          * @type int
17661          * @static
17662          */
17663         mode: 0,
17664
17665         /**
17666          * Runs method on all drag and drop objects
17667          * @method _execOnAll
17668          * @private
17669          * @static
17670          */
17671         _execOnAll: function(sMethod, args) {
17672             for (var i in this.ids) {
17673                 for (var j in this.ids[i]) {
17674                     var oDD = this.ids[i][j];
17675                     if (! this.isTypeOfDD(oDD)) {
17676                         continue;
17677                     }
17678                     oDD[sMethod].apply(oDD, args);
17679                 }
17680             }
17681         },
17682
17683         /**
17684          * Drag and drop initialization.  Sets up the global event handlers
17685          * @method _onLoad
17686          * @private
17687          * @static
17688          */
17689         _onLoad: function() {
17690
17691             this.init();
17692
17693             if (!Roo.isTouch) {
17694                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17695                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17696             }
17697             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17698             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17699             
17700             Event.on(window,   "unload",    this._onUnload, this, true);
17701             Event.on(window,   "resize",    this._onResize, this, true);
17702             // Event.on(window,   "mouseout",    this._test);
17703
17704         },
17705
17706         /**
17707          * Reset constraints on all drag and drop objs
17708          * @method _onResize
17709          * @private
17710          * @static
17711          */
17712         _onResize: function(e) {
17713             this._execOnAll("resetConstraints", []);
17714         },
17715
17716         /**
17717          * Lock all drag and drop functionality
17718          * @method lock
17719          * @static
17720          */
17721         lock: function() { this.locked = true; },
17722
17723         /**
17724          * Unlock all drag and drop functionality
17725          * @method unlock
17726          * @static
17727          */
17728         unlock: function() { this.locked = false; },
17729
17730         /**
17731          * Is drag and drop locked?
17732          * @method isLocked
17733          * @return {boolean} True if drag and drop is locked, false otherwise.
17734          * @static
17735          */
17736         isLocked: function() { return this.locked; },
17737
17738         /**
17739          * Location cache that is set for all drag drop objects when a drag is
17740          * initiated, cleared when the drag is finished.
17741          * @property locationCache
17742          * @private
17743          * @static
17744          */
17745         locationCache: {},
17746
17747         /**
17748          * Set useCache to false if you want to force object the lookup of each
17749          * drag and drop linked element constantly during a drag.
17750          * @property useCache
17751          * @type boolean
17752          * @static
17753          */
17754         useCache: true,
17755
17756         /**
17757          * The number of pixels that the mouse needs to move after the
17758          * mousedown before the drag is initiated.  Default=3;
17759          * @property clickPixelThresh
17760          * @type int
17761          * @static
17762          */
17763         clickPixelThresh: 3,
17764
17765         /**
17766          * The number of milliseconds after the mousedown event to initiate the
17767          * drag if we don't get a mouseup event. Default=1000
17768          * @property clickTimeThresh
17769          * @type int
17770          * @static
17771          */
17772         clickTimeThresh: 350,
17773
17774         /**
17775          * Flag that indicates that either the drag pixel threshold or the
17776          * mousdown time threshold has been met
17777          * @property dragThreshMet
17778          * @type boolean
17779          * @private
17780          * @static
17781          */
17782         dragThreshMet: false,
17783
17784         /**
17785          * Timeout used for the click time threshold
17786          * @property clickTimeout
17787          * @type Object
17788          * @private
17789          * @static
17790          */
17791         clickTimeout: null,
17792
17793         /**
17794          * The X position of the mousedown event stored for later use when a
17795          * drag threshold is met.
17796          * @property startX
17797          * @type int
17798          * @private
17799          * @static
17800          */
17801         startX: 0,
17802
17803         /**
17804          * The Y position of the mousedown event stored for later use when a
17805          * drag threshold is met.
17806          * @property startY
17807          * @type int
17808          * @private
17809          * @static
17810          */
17811         startY: 0,
17812
17813         /**
17814          * Each DragDrop instance must be registered with the DragDropMgr.
17815          * This is executed in DragDrop.init()
17816          * @method regDragDrop
17817          * @param {DragDrop} oDD the DragDrop object to register
17818          * @param {String} sGroup the name of the group this element belongs to
17819          * @static
17820          */
17821         regDragDrop: function(oDD, sGroup) {
17822             if (!this.initialized) { this.init(); }
17823
17824             if (!this.ids[sGroup]) {
17825                 this.ids[sGroup] = {};
17826             }
17827             this.ids[sGroup][oDD.id] = oDD;
17828         },
17829
17830         /**
17831          * Removes the supplied dd instance from the supplied group. Executed
17832          * by DragDrop.removeFromGroup, so don't call this function directly.
17833          * @method removeDDFromGroup
17834          * @private
17835          * @static
17836          */
17837         removeDDFromGroup: function(oDD, sGroup) {
17838             if (!this.ids[sGroup]) {
17839                 this.ids[sGroup] = {};
17840             }
17841
17842             var obj = this.ids[sGroup];
17843             if (obj && obj[oDD.id]) {
17844                 delete obj[oDD.id];
17845             }
17846         },
17847
17848         /**
17849          * Unregisters a drag and drop item.  This is executed in
17850          * DragDrop.unreg, use that method instead of calling this directly.
17851          * @method _remove
17852          * @private
17853          * @static
17854          */
17855         _remove: function(oDD) {
17856             for (var g in oDD.groups) {
17857                 if (g && this.ids[g][oDD.id]) {
17858                     delete this.ids[g][oDD.id];
17859                 }
17860             }
17861             delete this.handleIds[oDD.id];
17862         },
17863
17864         /**
17865          * Each DragDrop handle element must be registered.  This is done
17866          * automatically when executing DragDrop.setHandleElId()
17867          * @method regHandle
17868          * @param {String} sDDId the DragDrop id this element is a handle for
17869          * @param {String} sHandleId the id of the element that is the drag
17870          * handle
17871          * @static
17872          */
17873         regHandle: function(sDDId, sHandleId) {
17874             if (!this.handleIds[sDDId]) {
17875                 this.handleIds[sDDId] = {};
17876             }
17877             this.handleIds[sDDId][sHandleId] = sHandleId;
17878         },
17879
17880         /**
17881          * Utility function to determine if a given element has been
17882          * registered as a drag drop item.
17883          * @method isDragDrop
17884          * @param {String} id the element id to check
17885          * @return {boolean} true if this element is a DragDrop item,
17886          * false otherwise
17887          * @static
17888          */
17889         isDragDrop: function(id) {
17890             return ( this.getDDById(id) ) ? true : false;
17891         },
17892
17893         /**
17894          * Returns the drag and drop instances that are in all groups the
17895          * passed in instance belongs to.
17896          * @method getRelated
17897          * @param {DragDrop} p_oDD the obj to get related data for
17898          * @param {boolean} bTargetsOnly if true, only return targetable objs
17899          * @return {DragDrop[]} the related instances
17900          * @static
17901          */
17902         getRelated: function(p_oDD, bTargetsOnly) {
17903             var oDDs = [];
17904             for (var i in p_oDD.groups) {
17905                 for (j in this.ids[i]) {
17906                     var dd = this.ids[i][j];
17907                     if (! this.isTypeOfDD(dd)) {
17908                         continue;
17909                     }
17910                     if (!bTargetsOnly || dd.isTarget) {
17911                         oDDs[oDDs.length] = dd;
17912                     }
17913                 }
17914             }
17915
17916             return oDDs;
17917         },
17918
17919         /**
17920          * Returns true if the specified dd target is a legal target for
17921          * the specifice drag obj
17922          * @method isLegalTarget
17923          * @param {DragDrop} the drag obj
17924          * @param {DragDrop} the target
17925          * @return {boolean} true if the target is a legal target for the
17926          * dd obj
17927          * @static
17928          */
17929         isLegalTarget: function (oDD, oTargetDD) {
17930             var targets = this.getRelated(oDD, true);
17931             for (var i=0, len=targets.length;i<len;++i) {
17932                 if (targets[i].id == oTargetDD.id) {
17933                     return true;
17934                 }
17935             }
17936
17937             return false;
17938         },
17939
17940         /**
17941          * My goal is to be able to transparently determine if an object is
17942          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17943          * returns "object", oDD.constructor.toString() always returns
17944          * "DragDrop" and not the name of the subclass.  So for now it just
17945          * evaluates a well-known variable in DragDrop.
17946          * @method isTypeOfDD
17947          * @param {Object} the object to evaluate
17948          * @return {boolean} true if typeof oDD = DragDrop
17949          * @static
17950          */
17951         isTypeOfDD: function (oDD) {
17952             return (oDD && oDD.__ygDragDrop);
17953         },
17954
17955         /**
17956          * Utility function to determine if a given element has been
17957          * registered as a drag drop handle for the given Drag Drop object.
17958          * @method isHandle
17959          * @param {String} id the element id to check
17960          * @return {boolean} true if this element is a DragDrop handle, false
17961          * otherwise
17962          * @static
17963          */
17964         isHandle: function(sDDId, sHandleId) {
17965             return ( this.handleIds[sDDId] &&
17966                             this.handleIds[sDDId][sHandleId] );
17967         },
17968
17969         /**
17970          * Returns the DragDrop instance for a given id
17971          * @method getDDById
17972          * @param {String} id the id of the DragDrop object
17973          * @return {DragDrop} the drag drop object, null if it is not found
17974          * @static
17975          */
17976         getDDById: function(id) {
17977             for (var i in this.ids) {
17978                 if (this.ids[i][id]) {
17979                     return this.ids[i][id];
17980                 }
17981             }
17982             return null;
17983         },
17984
17985         /**
17986          * Fired after a registered DragDrop object gets the mousedown event.
17987          * Sets up the events required to track the object being dragged
17988          * @method handleMouseDown
17989          * @param {Event} e the event
17990          * @param oDD the DragDrop object being dragged
17991          * @private
17992          * @static
17993          */
17994         handleMouseDown: function(e, oDD) {
17995             if(Roo.QuickTips){
17996                 Roo.QuickTips.disable();
17997             }
17998             this.currentTarget = e.getTarget();
17999
18000             this.dragCurrent = oDD;
18001
18002             var el = oDD.getEl();
18003
18004             // track start position
18005             this.startX = e.getPageX();
18006             this.startY = e.getPageY();
18007
18008             this.deltaX = this.startX - el.offsetLeft;
18009             this.deltaY = this.startY - el.offsetTop;
18010
18011             this.dragThreshMet = false;
18012
18013             this.clickTimeout = setTimeout(
18014                     function() {
18015                         var DDM = Roo.dd.DDM;
18016                         DDM.startDrag(DDM.startX, DDM.startY);
18017                     },
18018                     this.clickTimeThresh );
18019         },
18020
18021         /**
18022          * Fired when either the drag pixel threshol or the mousedown hold
18023          * time threshold has been met.
18024          * @method startDrag
18025          * @param x {int} the X position of the original mousedown
18026          * @param y {int} the Y position of the original mousedown
18027          * @static
18028          */
18029         startDrag: function(x, y) {
18030             clearTimeout(this.clickTimeout);
18031             if (this.dragCurrent) {
18032                 this.dragCurrent.b4StartDrag(x, y);
18033                 this.dragCurrent.startDrag(x, y);
18034             }
18035             this.dragThreshMet = true;
18036         },
18037
18038         /**
18039          * Internal function to handle the mouseup event.  Will be invoked
18040          * from the context of the document.
18041          * @method handleMouseUp
18042          * @param {Event} e the event
18043          * @private
18044          * @static
18045          */
18046         handleMouseUp: function(e) {
18047
18048             if(Roo.QuickTips){
18049                 Roo.QuickTips.enable();
18050             }
18051             if (! this.dragCurrent) {
18052                 return;
18053             }
18054
18055             clearTimeout(this.clickTimeout);
18056
18057             if (this.dragThreshMet) {
18058                 this.fireEvents(e, true);
18059             } else {
18060             }
18061
18062             this.stopDrag(e);
18063
18064             this.stopEvent(e);
18065         },
18066
18067         /**
18068          * Utility to stop event propagation and event default, if these
18069          * features are turned on.
18070          * @method stopEvent
18071          * @param {Event} e the event as returned by this.getEvent()
18072          * @static
18073          */
18074         stopEvent: function(e){
18075             if(this.stopPropagation) {
18076                 e.stopPropagation();
18077             }
18078
18079             if (this.preventDefault) {
18080                 e.preventDefault();
18081             }
18082         },
18083
18084         /**
18085          * Internal function to clean up event handlers after the drag
18086          * operation is complete
18087          * @method stopDrag
18088          * @param {Event} e the event
18089          * @private
18090          * @static
18091          */
18092         stopDrag: function(e) {
18093             // Fire the drag end event for the item that was dragged
18094             if (this.dragCurrent) {
18095                 if (this.dragThreshMet) {
18096                     this.dragCurrent.b4EndDrag(e);
18097                     this.dragCurrent.endDrag(e);
18098                 }
18099
18100                 this.dragCurrent.onMouseUp(e);
18101             }
18102
18103             this.dragCurrent = null;
18104             this.dragOvers = {};
18105         },
18106
18107         /**
18108          * Internal function to handle the mousemove event.  Will be invoked
18109          * from the context of the html element.
18110          *
18111          * @TODO figure out what we can do about mouse events lost when the
18112          * user drags objects beyond the window boundary.  Currently we can
18113          * detect this in internet explorer by verifying that the mouse is
18114          * down during the mousemove event.  Firefox doesn't give us the
18115          * button state on the mousemove event.
18116          * @method handleMouseMove
18117          * @param {Event} e the event
18118          * @private
18119          * @static
18120          */
18121         handleMouseMove: function(e) {
18122             if (! this.dragCurrent) {
18123                 return true;
18124             }
18125
18126             // var button = e.which || e.button;
18127
18128             // check for IE mouseup outside of page boundary
18129             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18130                 this.stopEvent(e);
18131                 return this.handleMouseUp(e);
18132             }
18133
18134             if (!this.dragThreshMet) {
18135                 var diffX = Math.abs(this.startX - e.getPageX());
18136                 var diffY = Math.abs(this.startY - e.getPageY());
18137                 if (diffX > this.clickPixelThresh ||
18138                             diffY > this.clickPixelThresh) {
18139                     this.startDrag(this.startX, this.startY);
18140                 }
18141             }
18142
18143             if (this.dragThreshMet) {
18144                 this.dragCurrent.b4Drag(e);
18145                 this.dragCurrent.onDrag(e);
18146                 if(!this.dragCurrent.moveOnly){
18147                     this.fireEvents(e, false);
18148                 }
18149             }
18150
18151             this.stopEvent(e);
18152
18153             return true;
18154         },
18155
18156         /**
18157          * Iterates over all of the DragDrop elements to find ones we are
18158          * hovering over or dropping on
18159          * @method fireEvents
18160          * @param {Event} e the event
18161          * @param {boolean} isDrop is this a drop op or a mouseover op?
18162          * @private
18163          * @static
18164          */
18165         fireEvents: function(e, isDrop) {
18166             var dc = this.dragCurrent;
18167
18168             // If the user did the mouse up outside of the window, we could
18169             // get here even though we have ended the drag.
18170             if (!dc || dc.isLocked()) {
18171                 return;
18172             }
18173
18174             var pt = e.getPoint();
18175
18176             // cache the previous dragOver array
18177             var oldOvers = [];
18178
18179             var outEvts   = [];
18180             var overEvts  = [];
18181             var dropEvts  = [];
18182             var enterEvts = [];
18183
18184             // Check to see if the object(s) we were hovering over is no longer
18185             // being hovered over so we can fire the onDragOut event
18186             for (var i in this.dragOvers) {
18187
18188                 var ddo = this.dragOvers[i];
18189
18190                 if (! this.isTypeOfDD(ddo)) {
18191                     continue;
18192                 }
18193
18194                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18195                     outEvts.push( ddo );
18196                 }
18197
18198                 oldOvers[i] = true;
18199                 delete this.dragOvers[i];
18200             }
18201
18202             for (var sGroup in dc.groups) {
18203
18204                 if ("string" != typeof sGroup) {
18205                     continue;
18206                 }
18207
18208                 for (i in this.ids[sGroup]) {
18209                     var oDD = this.ids[sGroup][i];
18210                     if (! this.isTypeOfDD(oDD)) {
18211                         continue;
18212                     }
18213
18214                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18215                         if (this.isOverTarget(pt, oDD, this.mode)) {
18216                             // look for drop interactions
18217                             if (isDrop) {
18218                                 dropEvts.push( oDD );
18219                             // look for drag enter and drag over interactions
18220                             } else {
18221
18222                                 // initial drag over: dragEnter fires
18223                                 if (!oldOvers[oDD.id]) {
18224                                     enterEvts.push( oDD );
18225                                 // subsequent drag overs: dragOver fires
18226                                 } else {
18227                                     overEvts.push( oDD );
18228                                 }
18229
18230                                 this.dragOvers[oDD.id] = oDD;
18231                             }
18232                         }
18233                     }
18234                 }
18235             }
18236
18237             if (this.mode) {
18238                 if (outEvts.length) {
18239                     dc.b4DragOut(e, outEvts);
18240                     dc.onDragOut(e, outEvts);
18241                 }
18242
18243                 if (enterEvts.length) {
18244                     dc.onDragEnter(e, enterEvts);
18245                 }
18246
18247                 if (overEvts.length) {
18248                     dc.b4DragOver(e, overEvts);
18249                     dc.onDragOver(e, overEvts);
18250                 }
18251
18252                 if (dropEvts.length) {
18253                     dc.b4DragDrop(e, dropEvts);
18254                     dc.onDragDrop(e, dropEvts);
18255                 }
18256
18257             } else {
18258                 // fire dragout events
18259                 var len = 0;
18260                 for (i=0, len=outEvts.length; i<len; ++i) {
18261                     dc.b4DragOut(e, outEvts[i].id);
18262                     dc.onDragOut(e, outEvts[i].id);
18263                 }
18264
18265                 // fire enter events
18266                 for (i=0,len=enterEvts.length; i<len; ++i) {
18267                     // dc.b4DragEnter(e, oDD.id);
18268                     dc.onDragEnter(e, enterEvts[i].id);
18269                 }
18270
18271                 // fire over events
18272                 for (i=0,len=overEvts.length; i<len; ++i) {
18273                     dc.b4DragOver(e, overEvts[i].id);
18274                     dc.onDragOver(e, overEvts[i].id);
18275                 }
18276
18277                 // fire drop events
18278                 for (i=0, len=dropEvts.length; i<len; ++i) {
18279                     dc.b4DragDrop(e, dropEvts[i].id);
18280                     dc.onDragDrop(e, dropEvts[i].id);
18281                 }
18282
18283             }
18284
18285             // notify about a drop that did not find a target
18286             if (isDrop && !dropEvts.length) {
18287                 dc.onInvalidDrop(e);
18288             }
18289
18290         },
18291
18292         /**
18293          * Helper function for getting the best match from the list of drag
18294          * and drop objects returned by the drag and drop events when we are
18295          * in INTERSECT mode.  It returns either the first object that the
18296          * cursor is over, or the object that has the greatest overlap with
18297          * the dragged element.
18298          * @method getBestMatch
18299          * @param  {DragDrop[]} dds The array of drag and drop objects
18300          * targeted
18301          * @return {DragDrop}       The best single match
18302          * @static
18303          */
18304         getBestMatch: function(dds) {
18305             var winner = null;
18306             // Return null if the input is not what we expect
18307             //if (!dds || !dds.length || dds.length == 0) {
18308                // winner = null;
18309             // If there is only one item, it wins
18310             //} else if (dds.length == 1) {
18311
18312             var len = dds.length;
18313
18314             if (len == 1) {
18315                 winner = dds[0];
18316             } else {
18317                 // Loop through the targeted items
18318                 for (var i=0; i<len; ++i) {
18319                     var dd = dds[i];
18320                     // If the cursor is over the object, it wins.  If the
18321                     // cursor is over multiple matches, the first one we come
18322                     // to wins.
18323                     if (dd.cursorIsOver) {
18324                         winner = dd;
18325                         break;
18326                     // Otherwise the object with the most overlap wins
18327                     } else {
18328                         if (!winner ||
18329                             winner.overlap.getArea() < dd.overlap.getArea()) {
18330                             winner = dd;
18331                         }
18332                     }
18333                 }
18334             }
18335
18336             return winner;
18337         },
18338
18339         /**
18340          * Refreshes the cache of the top-left and bottom-right points of the
18341          * drag and drop objects in the specified group(s).  This is in the
18342          * format that is stored in the drag and drop instance, so typical
18343          * usage is:
18344          * <code>
18345          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18346          * </code>
18347          * Alternatively:
18348          * <code>
18349          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18350          * </code>
18351          * @TODO this really should be an indexed array.  Alternatively this
18352          * method could accept both.
18353          * @method refreshCache
18354          * @param {Object} groups an associative array of groups to refresh
18355          * @static
18356          */
18357         refreshCache: function(groups) {
18358             for (var sGroup in groups) {
18359                 if ("string" != typeof sGroup) {
18360                     continue;
18361                 }
18362                 for (var i in this.ids[sGroup]) {
18363                     var oDD = this.ids[sGroup][i];
18364
18365                     if (this.isTypeOfDD(oDD)) {
18366                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18367                         var loc = this.getLocation(oDD);
18368                         if (loc) {
18369                             this.locationCache[oDD.id] = loc;
18370                         } else {
18371                             delete this.locationCache[oDD.id];
18372                             // this will unregister the drag and drop object if
18373                             // the element is not in a usable state
18374                             // oDD.unreg();
18375                         }
18376                     }
18377                 }
18378             }
18379         },
18380
18381         /**
18382          * This checks to make sure an element exists and is in the DOM.  The
18383          * main purpose is to handle cases where innerHTML is used to remove
18384          * drag and drop objects from the DOM.  IE provides an 'unspecified
18385          * error' when trying to access the offsetParent of such an element
18386          * @method verifyEl
18387          * @param {HTMLElement} el the element to check
18388          * @return {boolean} true if the element looks usable
18389          * @static
18390          */
18391         verifyEl: function(el) {
18392             if (el) {
18393                 var parent;
18394                 if(Roo.isIE){
18395                     try{
18396                         parent = el.offsetParent;
18397                     }catch(e){}
18398                 }else{
18399                     parent = el.offsetParent;
18400                 }
18401                 if (parent) {
18402                     return true;
18403                 }
18404             }
18405
18406             return false;
18407         },
18408
18409         /**
18410          * Returns a Region object containing the drag and drop element's position
18411          * and size, including the padding configured for it
18412          * @method getLocation
18413          * @param {DragDrop} oDD the drag and drop object to get the
18414          *                       location for
18415          * @return {Roo.lib.Region} a Region object representing the total area
18416          *                             the element occupies, including any padding
18417          *                             the instance is configured for.
18418          * @static
18419          */
18420         getLocation: function(oDD) {
18421             if (! this.isTypeOfDD(oDD)) {
18422                 return null;
18423             }
18424
18425             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18426
18427             try {
18428                 pos= Roo.lib.Dom.getXY(el);
18429             } catch (e) { }
18430
18431             if (!pos) {
18432                 return null;
18433             }
18434
18435             x1 = pos[0];
18436             x2 = x1 + el.offsetWidth;
18437             y1 = pos[1];
18438             y2 = y1 + el.offsetHeight;
18439
18440             t = y1 - oDD.padding[0];
18441             r = x2 + oDD.padding[1];
18442             b = y2 + oDD.padding[2];
18443             l = x1 - oDD.padding[3];
18444
18445             return new Roo.lib.Region( t, r, b, l );
18446         },
18447
18448         /**
18449          * Checks the cursor location to see if it over the target
18450          * @method isOverTarget
18451          * @param {Roo.lib.Point} pt The point to evaluate
18452          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18453          * @return {boolean} true if the mouse is over the target
18454          * @private
18455          * @static
18456          */
18457         isOverTarget: function(pt, oTarget, intersect) {
18458             // use cache if available
18459             var loc = this.locationCache[oTarget.id];
18460             if (!loc || !this.useCache) {
18461                 loc = this.getLocation(oTarget);
18462                 this.locationCache[oTarget.id] = loc;
18463
18464             }
18465
18466             if (!loc) {
18467                 return false;
18468             }
18469
18470             oTarget.cursorIsOver = loc.contains( pt );
18471
18472             // DragDrop is using this as a sanity check for the initial mousedown
18473             // in this case we are done.  In POINT mode, if the drag obj has no
18474             // contraints, we are also done. Otherwise we need to evaluate the
18475             // location of the target as related to the actual location of the
18476             // dragged element.
18477             var dc = this.dragCurrent;
18478             if (!dc || !dc.getTargetCoord ||
18479                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18480                 return oTarget.cursorIsOver;
18481             }
18482
18483             oTarget.overlap = null;
18484
18485             // Get the current location of the drag element, this is the
18486             // location of the mouse event less the delta that represents
18487             // where the original mousedown happened on the element.  We
18488             // need to consider constraints and ticks as well.
18489             var pos = dc.getTargetCoord(pt.x, pt.y);
18490
18491             var el = dc.getDragEl();
18492             var curRegion = new Roo.lib.Region( pos.y,
18493                                                    pos.x + el.offsetWidth,
18494                                                    pos.y + el.offsetHeight,
18495                                                    pos.x );
18496
18497             var overlap = curRegion.intersect(loc);
18498
18499             if (overlap) {
18500                 oTarget.overlap = overlap;
18501                 return (intersect) ? true : oTarget.cursorIsOver;
18502             } else {
18503                 return false;
18504             }
18505         },
18506
18507         /**
18508          * unload event handler
18509          * @method _onUnload
18510          * @private
18511          * @static
18512          */
18513         _onUnload: function(e, me) {
18514             Roo.dd.DragDropMgr.unregAll();
18515         },
18516
18517         /**
18518          * Cleans up the drag and drop events and objects.
18519          * @method unregAll
18520          * @private
18521          * @static
18522          */
18523         unregAll: function() {
18524
18525             if (this.dragCurrent) {
18526                 this.stopDrag();
18527                 this.dragCurrent = null;
18528             }
18529
18530             this._execOnAll("unreg", []);
18531
18532             for (i in this.elementCache) {
18533                 delete this.elementCache[i];
18534             }
18535
18536             this.elementCache = {};
18537             this.ids = {};
18538         },
18539
18540         /**
18541          * A cache of DOM elements
18542          * @property elementCache
18543          * @private
18544          * @static
18545          */
18546         elementCache: {},
18547
18548         /**
18549          * Get the wrapper for the DOM element specified
18550          * @method getElWrapper
18551          * @param {String} id the id of the element to get
18552          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18553          * @private
18554          * @deprecated This wrapper isn't that useful
18555          * @static
18556          */
18557         getElWrapper: function(id) {
18558             var oWrapper = this.elementCache[id];
18559             if (!oWrapper || !oWrapper.el) {
18560                 oWrapper = this.elementCache[id] =
18561                     new this.ElementWrapper(Roo.getDom(id));
18562             }
18563             return oWrapper;
18564         },
18565
18566         /**
18567          * Returns the actual DOM element
18568          * @method getElement
18569          * @param {String} id the id of the elment to get
18570          * @return {Object} The element
18571          * @deprecated use Roo.getDom instead
18572          * @static
18573          */
18574         getElement: function(id) {
18575             return Roo.getDom(id);
18576         },
18577
18578         /**
18579          * Returns the style property for the DOM element (i.e.,
18580          * document.getElById(id).style)
18581          * @method getCss
18582          * @param {String} id the id of the elment to get
18583          * @return {Object} The style property of the element
18584          * @deprecated use Roo.getDom instead
18585          * @static
18586          */
18587         getCss: function(id) {
18588             var el = Roo.getDom(id);
18589             return (el) ? el.style : null;
18590         },
18591
18592         /**
18593          * Inner class for cached elements
18594          * @class DragDropMgr.ElementWrapper
18595          * @for DragDropMgr
18596          * @private
18597          * @deprecated
18598          */
18599         ElementWrapper: function(el) {
18600                 /**
18601                  * The element
18602                  * @property el
18603                  */
18604                 this.el = el || null;
18605                 /**
18606                  * The element id
18607                  * @property id
18608                  */
18609                 this.id = this.el && el.id;
18610                 /**
18611                  * A reference to the style property
18612                  * @property css
18613                  */
18614                 this.css = this.el && el.style;
18615             },
18616
18617         /**
18618          * Returns the X position of an html element
18619          * @method getPosX
18620          * @param el the element for which to get the position
18621          * @return {int} the X coordinate
18622          * @for DragDropMgr
18623          * @deprecated use Roo.lib.Dom.getX instead
18624          * @static
18625          */
18626         getPosX: function(el) {
18627             return Roo.lib.Dom.getX(el);
18628         },
18629
18630         /**
18631          * Returns the Y position of an html element
18632          * @method getPosY
18633          * @param el the element for which to get the position
18634          * @return {int} the Y coordinate
18635          * @deprecated use Roo.lib.Dom.getY instead
18636          * @static
18637          */
18638         getPosY: function(el) {
18639             return Roo.lib.Dom.getY(el);
18640         },
18641
18642         /**
18643          * Swap two nodes.  In IE, we use the native method, for others we
18644          * emulate the IE behavior
18645          * @method swapNode
18646          * @param n1 the first node to swap
18647          * @param n2 the other node to swap
18648          * @static
18649          */
18650         swapNode: function(n1, n2) {
18651             if (n1.swapNode) {
18652                 n1.swapNode(n2);
18653             } else {
18654                 var p = n2.parentNode;
18655                 var s = n2.nextSibling;
18656
18657                 if (s == n1) {
18658                     p.insertBefore(n1, n2);
18659                 } else if (n2 == n1.nextSibling) {
18660                     p.insertBefore(n2, n1);
18661                 } else {
18662                     n1.parentNode.replaceChild(n2, n1);
18663                     p.insertBefore(n1, s);
18664                 }
18665             }
18666         },
18667
18668         /**
18669          * Returns the current scroll position
18670          * @method getScroll
18671          * @private
18672          * @static
18673          */
18674         getScroll: function () {
18675             var t, l, dde=document.documentElement, db=document.body;
18676             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18677                 t = dde.scrollTop;
18678                 l = dde.scrollLeft;
18679             } else if (db) {
18680                 t = db.scrollTop;
18681                 l = db.scrollLeft;
18682             } else {
18683
18684             }
18685             return { top: t, left: l };
18686         },
18687
18688         /**
18689          * Returns the specified element style property
18690          * @method getStyle
18691          * @param {HTMLElement} el          the element
18692          * @param {string}      styleProp   the style property
18693          * @return {string} The value of the style property
18694          * @deprecated use Roo.lib.Dom.getStyle
18695          * @static
18696          */
18697         getStyle: function(el, styleProp) {
18698             return Roo.fly(el).getStyle(styleProp);
18699         },
18700
18701         /**
18702          * Gets the scrollTop
18703          * @method getScrollTop
18704          * @return {int} the document's scrollTop
18705          * @static
18706          */
18707         getScrollTop: function () { return this.getScroll().top; },
18708
18709         /**
18710          * Gets the scrollLeft
18711          * @method getScrollLeft
18712          * @return {int} the document's scrollTop
18713          * @static
18714          */
18715         getScrollLeft: function () { return this.getScroll().left; },
18716
18717         /**
18718          * Sets the x/y position of an element to the location of the
18719          * target element.
18720          * @method moveToEl
18721          * @param {HTMLElement} moveEl      The element to move
18722          * @param {HTMLElement} targetEl    The position reference element
18723          * @static
18724          */
18725         moveToEl: function (moveEl, targetEl) {
18726             var aCoord = Roo.lib.Dom.getXY(targetEl);
18727             Roo.lib.Dom.setXY(moveEl, aCoord);
18728         },
18729
18730         /**
18731          * Numeric array sort function
18732          * @method numericSort
18733          * @static
18734          */
18735         numericSort: function(a, b) { return (a - b); },
18736
18737         /**
18738          * Internal counter
18739          * @property _timeoutCount
18740          * @private
18741          * @static
18742          */
18743         _timeoutCount: 0,
18744
18745         /**
18746          * Trying to make the load order less important.  Without this we get
18747          * an error if this file is loaded before the Event Utility.
18748          * @method _addListeners
18749          * @private
18750          * @static
18751          */
18752         _addListeners: function() {
18753             var DDM = Roo.dd.DDM;
18754             if ( Roo.lib.Event && document ) {
18755                 DDM._onLoad();
18756             } else {
18757                 if (DDM._timeoutCount > 2000) {
18758                 } else {
18759                     setTimeout(DDM._addListeners, 10);
18760                     if (document && document.body) {
18761                         DDM._timeoutCount += 1;
18762                     }
18763                 }
18764             }
18765         },
18766
18767         /**
18768          * Recursively searches the immediate parent and all child nodes for
18769          * the handle element in order to determine wheter or not it was
18770          * clicked.
18771          * @method handleWasClicked
18772          * @param node the html element to inspect
18773          * @static
18774          */
18775         handleWasClicked: function(node, id) {
18776             if (this.isHandle(id, node.id)) {
18777                 return true;
18778             } else {
18779                 // check to see if this is a text node child of the one we want
18780                 var p = node.parentNode;
18781
18782                 while (p) {
18783                     if (this.isHandle(id, p.id)) {
18784                         return true;
18785                     } else {
18786                         p = p.parentNode;
18787                     }
18788                 }
18789             }
18790
18791             return false;
18792         }
18793
18794     };
18795
18796 }();
18797
18798 // shorter alias, save a few bytes
18799 Roo.dd.DDM = Roo.dd.DragDropMgr;
18800 Roo.dd.DDM._addListeners();
18801
18802 }/*
18803  * Based on:
18804  * Ext JS Library 1.1.1
18805  * Copyright(c) 2006-2007, Ext JS, LLC.
18806  *
18807  * Originally Released Under LGPL - original licence link has changed is not relivant.
18808  *
18809  * Fork - LGPL
18810  * <script type="text/javascript">
18811  */
18812
18813 /**
18814  * @class Roo.dd.DD
18815  * A DragDrop implementation where the linked element follows the
18816  * mouse cursor during a drag.
18817  * @extends Roo.dd.DragDrop
18818  * @constructor
18819  * @param {String} id the id of the linked element
18820  * @param {String} sGroup the group of related DragDrop items
18821  * @param {object} config an object containing configurable attributes
18822  *                Valid properties for DD:
18823  *                    scroll
18824  */
18825 Roo.dd.DD = function(id, sGroup, config) {
18826     if (id) {
18827         this.init(id, sGroup, config);
18828     }
18829 };
18830
18831 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18832
18833     /**
18834      * When set to true, the utility automatically tries to scroll the browser
18835      * window wehn a drag and drop element is dragged near the viewport boundary.
18836      * Defaults to true.
18837      * @property scroll
18838      * @type boolean
18839      */
18840     scroll: true,
18841
18842     /**
18843      * Sets the pointer offset to the distance between the linked element's top
18844      * left corner and the location the element was clicked
18845      * @method autoOffset
18846      * @param {int} iPageX the X coordinate of the click
18847      * @param {int} iPageY the Y coordinate of the click
18848      */
18849     autoOffset: function(iPageX, iPageY) {
18850         var x = iPageX - this.startPageX;
18851         var y = iPageY - this.startPageY;
18852         this.setDelta(x, y);
18853     },
18854
18855     /**
18856      * Sets the pointer offset.  You can call this directly to force the
18857      * offset to be in a particular location (e.g., pass in 0,0 to set it
18858      * to the center of the object)
18859      * @method setDelta
18860      * @param {int} iDeltaX the distance from the left
18861      * @param {int} iDeltaY the distance from the top
18862      */
18863     setDelta: function(iDeltaX, iDeltaY) {
18864         this.deltaX = iDeltaX;
18865         this.deltaY = iDeltaY;
18866     },
18867
18868     /**
18869      * Sets the drag element to the location of the mousedown or click event,
18870      * maintaining the cursor location relative to the location on the element
18871      * that was clicked.  Override this if you want to place the element in a
18872      * location other than where the cursor is.
18873      * @method setDragElPos
18874      * @param {int} iPageX the X coordinate of the mousedown or drag event
18875      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18876      */
18877     setDragElPos: function(iPageX, iPageY) {
18878         // the first time we do this, we are going to check to make sure
18879         // the element has css positioning
18880
18881         var el = this.getDragEl();
18882         this.alignElWithMouse(el, iPageX, iPageY);
18883     },
18884
18885     /**
18886      * Sets the element to the location of the mousedown or click event,
18887      * maintaining the cursor location relative to the location on the element
18888      * that was clicked.  Override this if you want to place the element in a
18889      * location other than where the cursor is.
18890      * @method alignElWithMouse
18891      * @param {HTMLElement} el the element to move
18892      * @param {int} iPageX the X coordinate of the mousedown or drag event
18893      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18894      */
18895     alignElWithMouse: function(el, iPageX, iPageY) {
18896         var oCoord = this.getTargetCoord(iPageX, iPageY);
18897         var fly = el.dom ? el : Roo.fly(el);
18898         if (!this.deltaSetXY) {
18899             var aCoord = [oCoord.x, oCoord.y];
18900             fly.setXY(aCoord);
18901             var newLeft = fly.getLeft(true);
18902             var newTop  = fly.getTop(true);
18903             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18904         } else {
18905             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18906         }
18907
18908         this.cachePosition(oCoord.x, oCoord.y);
18909         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18910         return oCoord;
18911     },
18912
18913     /**
18914      * Saves the most recent position so that we can reset the constraints and
18915      * tick marks on-demand.  We need to know this so that we can calculate the
18916      * number of pixels the element is offset from its original position.
18917      * @method cachePosition
18918      * @param iPageX the current x position (optional, this just makes it so we
18919      * don't have to look it up again)
18920      * @param iPageY the current y position (optional, this just makes it so we
18921      * don't have to look it up again)
18922      */
18923     cachePosition: function(iPageX, iPageY) {
18924         if (iPageX) {
18925             this.lastPageX = iPageX;
18926             this.lastPageY = iPageY;
18927         } else {
18928             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18929             this.lastPageX = aCoord[0];
18930             this.lastPageY = aCoord[1];
18931         }
18932     },
18933
18934     /**
18935      * Auto-scroll the window if the dragged object has been moved beyond the
18936      * visible window boundary.
18937      * @method autoScroll
18938      * @param {int} x the drag element's x position
18939      * @param {int} y the drag element's y position
18940      * @param {int} h the height of the drag element
18941      * @param {int} w the width of the drag element
18942      * @private
18943      */
18944     autoScroll: function(x, y, h, w) {
18945
18946         if (this.scroll) {
18947             // The client height
18948             var clientH = Roo.lib.Dom.getViewWidth();
18949
18950             // The client width
18951             var clientW = Roo.lib.Dom.getViewHeight();
18952
18953             // The amt scrolled down
18954             var st = this.DDM.getScrollTop();
18955
18956             // The amt scrolled right
18957             var sl = this.DDM.getScrollLeft();
18958
18959             // Location of the bottom of the element
18960             var bot = h + y;
18961
18962             // Location of the right of the element
18963             var right = w + x;
18964
18965             // The distance from the cursor to the bottom of the visible area,
18966             // adjusted so that we don't scroll if the cursor is beyond the
18967             // element drag constraints
18968             var toBot = (clientH + st - y - this.deltaY);
18969
18970             // The distance from the cursor to the right of the visible area
18971             var toRight = (clientW + sl - x - this.deltaX);
18972
18973
18974             // How close to the edge the cursor must be before we scroll
18975             // var thresh = (document.all) ? 100 : 40;
18976             var thresh = 40;
18977
18978             // How many pixels to scroll per autoscroll op.  This helps to reduce
18979             // clunky scrolling. IE is more sensitive about this ... it needs this
18980             // value to be higher.
18981             var scrAmt = (document.all) ? 80 : 30;
18982
18983             // Scroll down if we are near the bottom of the visible page and the
18984             // obj extends below the crease
18985             if ( bot > clientH && toBot < thresh ) {
18986                 window.scrollTo(sl, st + scrAmt);
18987             }
18988
18989             // Scroll up if the window is scrolled down and the top of the object
18990             // goes above the top border
18991             if ( y < st && st > 0 && y - st < thresh ) {
18992                 window.scrollTo(sl, st - scrAmt);
18993             }
18994
18995             // Scroll right if the obj is beyond the right border and the cursor is
18996             // near the border.
18997             if ( right > clientW && toRight < thresh ) {
18998                 window.scrollTo(sl + scrAmt, st);
18999             }
19000
19001             // Scroll left if the window has been scrolled to the right and the obj
19002             // extends past the left border
19003             if ( x < sl && sl > 0 && x - sl < thresh ) {
19004                 window.scrollTo(sl - scrAmt, st);
19005             }
19006         }
19007     },
19008
19009     /**
19010      * Finds the location the element should be placed if we want to move
19011      * it to where the mouse location less the click offset would place us.
19012      * @method getTargetCoord
19013      * @param {int} iPageX the X coordinate of the click
19014      * @param {int} iPageY the Y coordinate of the click
19015      * @return an object that contains the coordinates (Object.x and Object.y)
19016      * @private
19017      */
19018     getTargetCoord: function(iPageX, iPageY) {
19019
19020
19021         var x = iPageX - this.deltaX;
19022         var y = iPageY - this.deltaY;
19023
19024         if (this.constrainX) {
19025             if (x < this.minX) { x = this.minX; }
19026             if (x > this.maxX) { x = this.maxX; }
19027         }
19028
19029         if (this.constrainY) {
19030             if (y < this.minY) { y = this.minY; }
19031             if (y > this.maxY) { y = this.maxY; }
19032         }
19033
19034         x = this.getTick(x, this.xTicks);
19035         y = this.getTick(y, this.yTicks);
19036
19037
19038         return {x:x, y:y};
19039     },
19040
19041     /*
19042      * Sets up config options specific to this class. Overrides
19043      * Roo.dd.DragDrop, but all versions of this method through the
19044      * inheritance chain are called
19045      */
19046     applyConfig: function() {
19047         Roo.dd.DD.superclass.applyConfig.call(this);
19048         this.scroll = (this.config.scroll !== false);
19049     },
19050
19051     /*
19052      * Event that fires prior to the onMouseDown event.  Overrides
19053      * Roo.dd.DragDrop.
19054      */
19055     b4MouseDown: function(e) {
19056         // this.resetConstraints();
19057         this.autoOffset(e.getPageX(),
19058                             e.getPageY());
19059     },
19060
19061     /*
19062      * Event that fires prior to the onDrag event.  Overrides
19063      * Roo.dd.DragDrop.
19064      */
19065     b4Drag: function(e) {
19066         this.setDragElPos(e.getPageX(),
19067                             e.getPageY());
19068     },
19069
19070     toString: function() {
19071         return ("DD " + this.id);
19072     }
19073
19074     //////////////////////////////////////////////////////////////////////////
19075     // Debugging ygDragDrop events that can be overridden
19076     //////////////////////////////////////////////////////////////////////////
19077     /*
19078     startDrag: function(x, y) {
19079     },
19080
19081     onDrag: function(e) {
19082     },
19083
19084     onDragEnter: function(e, id) {
19085     },
19086
19087     onDragOver: function(e, id) {
19088     },
19089
19090     onDragOut: function(e, id) {
19091     },
19092
19093     onDragDrop: function(e, id) {
19094     },
19095
19096     endDrag: function(e) {
19097     }
19098
19099     */
19100
19101 });/*
19102  * Based on:
19103  * Ext JS Library 1.1.1
19104  * Copyright(c) 2006-2007, Ext JS, LLC.
19105  *
19106  * Originally Released Under LGPL - original licence link has changed is not relivant.
19107  *
19108  * Fork - LGPL
19109  * <script type="text/javascript">
19110  */
19111
19112 /**
19113  * @class Roo.dd.DDProxy
19114  * A DragDrop implementation that inserts an empty, bordered div into
19115  * the document that follows the cursor during drag operations.  At the time of
19116  * the click, the frame div is resized to the dimensions of the linked html
19117  * element, and moved to the exact location of the linked element.
19118  *
19119  * References to the "frame" element refer to the single proxy element that
19120  * was created to be dragged in place of all DDProxy elements on the
19121  * page.
19122  *
19123  * @extends Roo.dd.DD
19124  * @constructor
19125  * @param {String} id the id of the linked html element
19126  * @param {String} sGroup the group of related DragDrop objects
19127  * @param {object} config an object containing configurable attributes
19128  *                Valid properties for DDProxy in addition to those in DragDrop:
19129  *                   resizeFrame, centerFrame, dragElId
19130  */
19131 Roo.dd.DDProxy = function(id, sGroup, config) {
19132     if (id) {
19133         this.init(id, sGroup, config);
19134         this.initFrame();
19135     }
19136 };
19137
19138 /**
19139  * The default drag frame div id
19140  * @property Roo.dd.DDProxy.dragElId
19141  * @type String
19142  * @static
19143  */
19144 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19145
19146 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19147
19148     /**
19149      * By default we resize the drag frame to be the same size as the element
19150      * we want to drag (this is to get the frame effect).  We can turn it off
19151      * if we want a different behavior.
19152      * @property resizeFrame
19153      * @type boolean
19154      */
19155     resizeFrame: true,
19156
19157     /**
19158      * By default the frame is positioned exactly where the drag element is, so
19159      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19160      * you do not have constraints on the obj is to have the drag frame centered
19161      * around the cursor.  Set centerFrame to true for this effect.
19162      * @property centerFrame
19163      * @type boolean
19164      */
19165     centerFrame: false,
19166
19167     /**
19168      * Creates the proxy element if it does not yet exist
19169      * @method createFrame
19170      */
19171     createFrame: function() {
19172         var self = this;
19173         var body = document.body;
19174
19175         if (!body || !body.firstChild) {
19176             setTimeout( function() { self.createFrame(); }, 50 );
19177             return;
19178         }
19179
19180         var div = this.getDragEl();
19181
19182         if (!div) {
19183             div    = document.createElement("div");
19184             div.id = this.dragElId;
19185             var s  = div.style;
19186
19187             s.position   = "absolute";
19188             s.visibility = "hidden";
19189             s.cursor     = "move";
19190             s.border     = "2px solid #aaa";
19191             s.zIndex     = 999;
19192
19193             // appendChild can blow up IE if invoked prior to the window load event
19194             // while rendering a table.  It is possible there are other scenarios
19195             // that would cause this to happen as well.
19196             body.insertBefore(div, body.firstChild);
19197         }
19198     },
19199
19200     /**
19201      * Initialization for the drag frame element.  Must be called in the
19202      * constructor of all subclasses
19203      * @method initFrame
19204      */
19205     initFrame: function() {
19206         this.createFrame();
19207     },
19208
19209     applyConfig: function() {
19210         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19211
19212         this.resizeFrame = (this.config.resizeFrame !== false);
19213         this.centerFrame = (this.config.centerFrame);
19214         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19215     },
19216
19217     /**
19218      * Resizes the drag frame to the dimensions of the clicked object, positions
19219      * it over the object, and finally displays it
19220      * @method showFrame
19221      * @param {int} iPageX X click position
19222      * @param {int} iPageY Y click position
19223      * @private
19224      */
19225     showFrame: function(iPageX, iPageY) {
19226         var el = this.getEl();
19227         var dragEl = this.getDragEl();
19228         var s = dragEl.style;
19229
19230         this._resizeProxy();
19231
19232         if (this.centerFrame) {
19233             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19234                            Math.round(parseInt(s.height, 10)/2) );
19235         }
19236
19237         this.setDragElPos(iPageX, iPageY);
19238
19239         Roo.fly(dragEl).show();
19240     },
19241
19242     /**
19243      * The proxy is automatically resized to the dimensions of the linked
19244      * element when a drag is initiated, unless resizeFrame is set to false
19245      * @method _resizeProxy
19246      * @private
19247      */
19248     _resizeProxy: function() {
19249         if (this.resizeFrame) {
19250             var el = this.getEl();
19251             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19252         }
19253     },
19254
19255     // overrides Roo.dd.DragDrop
19256     b4MouseDown: function(e) {
19257         var x = e.getPageX();
19258         var y = e.getPageY();
19259         this.autoOffset(x, y);
19260         this.setDragElPos(x, y);
19261     },
19262
19263     // overrides Roo.dd.DragDrop
19264     b4StartDrag: function(x, y) {
19265         // show the drag frame
19266         this.showFrame(x, y);
19267     },
19268
19269     // overrides Roo.dd.DragDrop
19270     b4EndDrag: function(e) {
19271         Roo.fly(this.getDragEl()).hide();
19272     },
19273
19274     // overrides Roo.dd.DragDrop
19275     // By default we try to move the element to the last location of the frame.
19276     // This is so that the default behavior mirrors that of Roo.dd.DD.
19277     endDrag: function(e) {
19278
19279         var lel = this.getEl();
19280         var del = this.getDragEl();
19281
19282         // Show the drag frame briefly so we can get its position
19283         del.style.visibility = "";
19284
19285         this.beforeMove();
19286         // Hide the linked element before the move to get around a Safari
19287         // rendering bug.
19288         lel.style.visibility = "hidden";
19289         Roo.dd.DDM.moveToEl(lel, del);
19290         del.style.visibility = "hidden";
19291         lel.style.visibility = "";
19292
19293         this.afterDrag();
19294     },
19295
19296     beforeMove : function(){
19297
19298     },
19299
19300     afterDrag : function(){
19301
19302     },
19303
19304     toString: function() {
19305         return ("DDProxy " + this.id);
19306     }
19307
19308 });
19309 /*
19310  * Based on:
19311  * Ext JS Library 1.1.1
19312  * Copyright(c) 2006-2007, Ext JS, LLC.
19313  *
19314  * Originally Released Under LGPL - original licence link has changed is not relivant.
19315  *
19316  * Fork - LGPL
19317  * <script type="text/javascript">
19318  */
19319
19320  /**
19321  * @class Roo.dd.DDTarget
19322  * A DragDrop implementation that does not move, but can be a drop
19323  * target.  You would get the same result by simply omitting implementation
19324  * for the event callbacks, but this way we reduce the processing cost of the
19325  * event listener and the callbacks.
19326  * @extends Roo.dd.DragDrop
19327  * @constructor
19328  * @param {String} id the id of the element that is a drop target
19329  * @param {String} sGroup the group of related DragDrop objects
19330  * @param {object} config an object containing configurable attributes
19331  *                 Valid properties for DDTarget in addition to those in
19332  *                 DragDrop:
19333  *                    none
19334  */
19335 Roo.dd.DDTarget = function(id, sGroup, config) {
19336     if (id) {
19337         this.initTarget(id, sGroup, config);
19338     }
19339     if (config.listeners || config.events) { 
19340        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19341             listeners : config.listeners || {}, 
19342             events : config.events || {} 
19343         });    
19344     }
19345 };
19346
19347 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19348 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19349     toString: function() {
19350         return ("DDTarget " + this.id);
19351     }
19352 });
19353 /*
19354  * Based on:
19355  * Ext JS Library 1.1.1
19356  * Copyright(c) 2006-2007, Ext JS, LLC.
19357  *
19358  * Originally Released Under LGPL - original licence link has changed is not relivant.
19359  *
19360  * Fork - LGPL
19361  * <script type="text/javascript">
19362  */
19363  
19364
19365 /**
19366  * @class Roo.dd.ScrollManager
19367  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19368  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19369  * @singleton
19370  */
19371 Roo.dd.ScrollManager = function(){
19372     var ddm = Roo.dd.DragDropMgr;
19373     var els = {};
19374     var dragEl = null;
19375     var proc = {};
19376     
19377     
19378     
19379     var onStop = function(e){
19380         dragEl = null;
19381         clearProc();
19382     };
19383     
19384     var triggerRefresh = function(){
19385         if(ddm.dragCurrent){
19386              ddm.refreshCache(ddm.dragCurrent.groups);
19387         }
19388     };
19389     
19390     var doScroll = function(){
19391         if(ddm.dragCurrent){
19392             var dds = Roo.dd.ScrollManager;
19393             if(!dds.animate){
19394                 if(proc.el.scroll(proc.dir, dds.increment)){
19395                     triggerRefresh();
19396                 }
19397             }else{
19398                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19399             }
19400         }
19401     };
19402     
19403     var clearProc = function(){
19404         if(proc.id){
19405             clearInterval(proc.id);
19406         }
19407         proc.id = 0;
19408         proc.el = null;
19409         proc.dir = "";
19410     };
19411     
19412     var startProc = function(el, dir){
19413          Roo.log('scroll startproc');
19414         clearProc();
19415         proc.el = el;
19416         proc.dir = dir;
19417         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19418     };
19419     
19420     var onFire = function(e, isDrop){
19421        
19422         if(isDrop || !ddm.dragCurrent){ return; }
19423         var dds = Roo.dd.ScrollManager;
19424         if(!dragEl || dragEl != ddm.dragCurrent){
19425             dragEl = ddm.dragCurrent;
19426             // refresh regions on drag start
19427             dds.refreshCache();
19428         }
19429         
19430         var xy = Roo.lib.Event.getXY(e);
19431         var pt = new Roo.lib.Point(xy[0], xy[1]);
19432         for(var id in els){
19433             var el = els[id], r = el._region;
19434             if(r && r.contains(pt) && el.isScrollable()){
19435                 if(r.bottom - pt.y <= dds.thresh){
19436                     if(proc.el != el){
19437                         startProc(el, "down");
19438                     }
19439                     return;
19440                 }else if(r.right - pt.x <= dds.thresh){
19441                     if(proc.el != el){
19442                         startProc(el, "left");
19443                     }
19444                     return;
19445                 }else if(pt.y - r.top <= dds.thresh){
19446                     if(proc.el != el){
19447                         startProc(el, "up");
19448                     }
19449                     return;
19450                 }else if(pt.x - r.left <= dds.thresh){
19451                     if(proc.el != el){
19452                         startProc(el, "right");
19453                     }
19454                     return;
19455                 }
19456             }
19457         }
19458         clearProc();
19459     };
19460     
19461     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19462     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19463     
19464     return {
19465         /**
19466          * Registers new overflow element(s) to auto scroll
19467          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19468          */
19469         register : function(el){
19470             if(el instanceof Array){
19471                 for(var i = 0, len = el.length; i < len; i++) {
19472                         this.register(el[i]);
19473                 }
19474             }else{
19475                 el = Roo.get(el);
19476                 els[el.id] = el;
19477             }
19478             Roo.dd.ScrollManager.els = els;
19479         },
19480         
19481         /**
19482          * Unregisters overflow element(s) so they are no longer scrolled
19483          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19484          */
19485         unregister : function(el){
19486             if(el instanceof Array){
19487                 for(var i = 0, len = el.length; i < len; i++) {
19488                         this.unregister(el[i]);
19489                 }
19490             }else{
19491                 el = Roo.get(el);
19492                 delete els[el.id];
19493             }
19494         },
19495         
19496         /**
19497          * The number of pixels from the edge of a container the pointer needs to be to 
19498          * trigger scrolling (defaults to 25)
19499          * @type Number
19500          */
19501         thresh : 25,
19502         
19503         /**
19504          * The number of pixels to scroll in each scroll increment (defaults to 50)
19505          * @type Number
19506          */
19507         increment : 100,
19508         
19509         /**
19510          * The frequency of scrolls in milliseconds (defaults to 500)
19511          * @type Number
19512          */
19513         frequency : 500,
19514         
19515         /**
19516          * True to animate the scroll (defaults to true)
19517          * @type Boolean
19518          */
19519         animate: true,
19520         
19521         /**
19522          * The animation duration in seconds - 
19523          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19524          * @type Number
19525          */
19526         animDuration: .4,
19527         
19528         /**
19529          * Manually trigger a cache refresh.
19530          */
19531         refreshCache : function(){
19532             for(var id in els){
19533                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19534                     els[id]._region = els[id].getRegion();
19535                 }
19536             }
19537         }
19538     };
19539 }();/*
19540  * Based on:
19541  * Ext JS Library 1.1.1
19542  * Copyright(c) 2006-2007, Ext JS, LLC.
19543  *
19544  * Originally Released Under LGPL - original licence link has changed is not relivant.
19545  *
19546  * Fork - LGPL
19547  * <script type="text/javascript">
19548  */
19549  
19550
19551 /**
19552  * @class Roo.dd.Registry
19553  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19554  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19555  * @singleton
19556  */
19557 Roo.dd.Registry = function(){
19558     var elements = {}; 
19559     var handles = {}; 
19560     var autoIdSeed = 0;
19561
19562     var getId = function(el, autogen){
19563         if(typeof el == "string"){
19564             return el;
19565         }
19566         var id = el.id;
19567         if(!id && autogen !== false){
19568             id = "roodd-" + (++autoIdSeed);
19569             el.id = id;
19570         }
19571         return id;
19572     };
19573     
19574     return {
19575     /**
19576      * Register a drag drop element
19577      * @param {String|HTMLElement} element The id or DOM node to register
19578      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19579      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19580      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19581      * populated in the data object (if applicable):
19582      * <pre>
19583 Value      Description<br />
19584 ---------  ------------------------------------------<br />
19585 handles    Array of DOM nodes that trigger dragging<br />
19586            for the element being registered<br />
19587 isHandle   True if the element passed in triggers<br />
19588            dragging itself, else false
19589 </pre>
19590      */
19591         register : function(el, data){
19592             data = data || {};
19593             if(typeof el == "string"){
19594                 el = document.getElementById(el);
19595             }
19596             data.ddel = el;
19597             elements[getId(el)] = data;
19598             if(data.isHandle !== false){
19599                 handles[data.ddel.id] = data;
19600             }
19601             if(data.handles){
19602                 var hs = data.handles;
19603                 for(var i = 0, len = hs.length; i < len; i++){
19604                         handles[getId(hs[i])] = data;
19605                 }
19606             }
19607         },
19608
19609     /**
19610      * Unregister a drag drop element
19611      * @param {String|HTMLElement}  element The id or DOM node to unregister
19612      */
19613         unregister : function(el){
19614             var id = getId(el, false);
19615             var data = elements[id];
19616             if(data){
19617                 delete elements[id];
19618                 if(data.handles){
19619                     var hs = data.handles;
19620                     for(var i = 0, len = hs.length; i < len; i++){
19621                         delete handles[getId(hs[i], false)];
19622                     }
19623                 }
19624             }
19625         },
19626
19627     /**
19628      * Returns the handle registered for a DOM Node by id
19629      * @param {String|HTMLElement} id The DOM node or id to look up
19630      * @return {Object} handle The custom handle data
19631      */
19632         getHandle : function(id){
19633             if(typeof id != "string"){ // must be element?
19634                 id = id.id;
19635             }
19636             return handles[id];
19637         },
19638
19639     /**
19640      * Returns the handle that is registered for the DOM node that is the target of the event
19641      * @param {Event} e The event
19642      * @return {Object} handle The custom handle data
19643      */
19644         getHandleFromEvent : function(e){
19645             var t = Roo.lib.Event.getTarget(e);
19646             return t ? handles[t.id] : null;
19647         },
19648
19649     /**
19650      * Returns a custom data object that is registered for a DOM node by id
19651      * @param {String|HTMLElement} id The DOM node or id to look up
19652      * @return {Object} data The custom data
19653      */
19654         getTarget : function(id){
19655             if(typeof id != "string"){ // must be element?
19656                 id = id.id;
19657             }
19658             return elements[id];
19659         },
19660
19661     /**
19662      * Returns a custom data object that is registered for the DOM node that is the target of the event
19663      * @param {Event} e The event
19664      * @return {Object} data The custom data
19665      */
19666         getTargetFromEvent : function(e){
19667             var t = Roo.lib.Event.getTarget(e);
19668             return t ? elements[t.id] || handles[t.id] : null;
19669         }
19670     };
19671 }();/*
19672  * Based on:
19673  * Ext JS Library 1.1.1
19674  * Copyright(c) 2006-2007, Ext JS, LLC.
19675  *
19676  * Originally Released Under LGPL - original licence link has changed is not relivant.
19677  *
19678  * Fork - LGPL
19679  * <script type="text/javascript">
19680  */
19681  
19682
19683 /**
19684  * @class Roo.dd.StatusProxy
19685  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19686  * default drag proxy used by all Roo.dd components.
19687  * @constructor
19688  * @param {Object} config
19689  */
19690 Roo.dd.StatusProxy = function(config){
19691     Roo.apply(this, config);
19692     this.id = this.id || Roo.id();
19693     this.el = new Roo.Layer({
19694         dh: {
19695             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19696                 {tag: "div", cls: "x-dd-drop-icon"},
19697                 {tag: "div", cls: "x-dd-drag-ghost"}
19698             ]
19699         }, 
19700         shadow: !config || config.shadow !== false
19701     });
19702     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19703     this.dropStatus = this.dropNotAllowed;
19704 };
19705
19706 Roo.dd.StatusProxy.prototype = {
19707     /**
19708      * @cfg {String} dropAllowed
19709      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19710      */
19711     dropAllowed : "x-dd-drop-ok",
19712     /**
19713      * @cfg {String} dropNotAllowed
19714      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19715      */
19716     dropNotAllowed : "x-dd-drop-nodrop",
19717
19718     /**
19719      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19720      * over the current target element.
19721      * @param {String} cssClass The css class for the new drop status indicator image
19722      */
19723     setStatus : function(cssClass){
19724         cssClass = cssClass || this.dropNotAllowed;
19725         if(this.dropStatus != cssClass){
19726             this.el.replaceClass(this.dropStatus, cssClass);
19727             this.dropStatus = cssClass;
19728         }
19729     },
19730
19731     /**
19732      * Resets the status indicator to the default dropNotAllowed value
19733      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19734      */
19735     reset : function(clearGhost){
19736         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19737         this.dropStatus = this.dropNotAllowed;
19738         if(clearGhost){
19739             this.ghost.update("");
19740         }
19741     },
19742
19743     /**
19744      * Updates the contents of the ghost element
19745      * @param {String} html The html that will replace the current innerHTML of the ghost element
19746      */
19747     update : function(html){
19748         if(typeof html == "string"){
19749             this.ghost.update(html);
19750         }else{
19751             this.ghost.update("");
19752             html.style.margin = "0";
19753             this.ghost.dom.appendChild(html);
19754         }
19755         // ensure float = none set?? cant remember why though.
19756         var el = this.ghost.dom.firstChild;
19757                 if(el){
19758                         Roo.fly(el).setStyle('float', 'none');
19759                 }
19760     },
19761     
19762     /**
19763      * Returns the underlying proxy {@link Roo.Layer}
19764      * @return {Roo.Layer} el
19765     */
19766     getEl : function(){
19767         return this.el;
19768     },
19769
19770     /**
19771      * Returns the ghost element
19772      * @return {Roo.Element} el
19773      */
19774     getGhost : function(){
19775         return this.ghost;
19776     },
19777
19778     /**
19779      * Hides the proxy
19780      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19781      */
19782     hide : function(clear){
19783         this.el.hide();
19784         if(clear){
19785             this.reset(true);
19786         }
19787     },
19788
19789     /**
19790      * Stops the repair animation if it's currently running
19791      */
19792     stop : function(){
19793         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19794             this.anim.stop();
19795         }
19796     },
19797
19798     /**
19799      * Displays this proxy
19800      */
19801     show : function(){
19802         this.el.show();
19803     },
19804
19805     /**
19806      * Force the Layer to sync its shadow and shim positions to the element
19807      */
19808     sync : function(){
19809         this.el.sync();
19810     },
19811
19812     /**
19813      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19814      * invalid drop operation by the item being dragged.
19815      * @param {Array} xy The XY position of the element ([x, y])
19816      * @param {Function} callback The function to call after the repair is complete
19817      * @param {Object} scope The scope in which to execute the callback
19818      */
19819     repair : function(xy, callback, scope){
19820         this.callback = callback;
19821         this.scope = scope;
19822         if(xy && this.animRepair !== false){
19823             this.el.addClass("x-dd-drag-repair");
19824             this.el.hideUnders(true);
19825             this.anim = this.el.shift({
19826                 duration: this.repairDuration || .5,
19827                 easing: 'easeOut',
19828                 xy: xy,
19829                 stopFx: true,
19830                 callback: this.afterRepair,
19831                 scope: this
19832             });
19833         }else{
19834             this.afterRepair();
19835         }
19836     },
19837
19838     // private
19839     afterRepair : function(){
19840         this.hide(true);
19841         if(typeof this.callback == "function"){
19842             this.callback.call(this.scope || this);
19843         }
19844         this.callback = null;
19845         this.scope = null;
19846     }
19847 };/*
19848  * Based on:
19849  * Ext JS Library 1.1.1
19850  * Copyright(c) 2006-2007, Ext JS, LLC.
19851  *
19852  * Originally Released Under LGPL - original licence link has changed is not relivant.
19853  *
19854  * Fork - LGPL
19855  * <script type="text/javascript">
19856  */
19857
19858 /**
19859  * @class Roo.dd.DragSource
19860  * @extends Roo.dd.DDProxy
19861  * A simple class that provides the basic implementation needed to make any element draggable.
19862  * @constructor
19863  * @param {String/HTMLElement/Element} el The container element
19864  * @param {Object} config
19865  */
19866 Roo.dd.DragSource = function(el, config){
19867     this.el = Roo.get(el);
19868     this.dragData = {};
19869     
19870     Roo.apply(this, config);
19871     
19872     if(!this.proxy){
19873         this.proxy = new Roo.dd.StatusProxy();
19874     }
19875
19876     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19877           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19878     
19879     this.dragging = false;
19880 };
19881
19882 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19883     /**
19884      * @cfg {String} dropAllowed
19885      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19886      */
19887     dropAllowed : "x-dd-drop-ok",
19888     /**
19889      * @cfg {String} dropNotAllowed
19890      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19891      */
19892     dropNotAllowed : "x-dd-drop-nodrop",
19893
19894     /**
19895      * Returns the data object associated with this drag source
19896      * @return {Object} data An object containing arbitrary data
19897      */
19898     getDragData : function(e){
19899         return this.dragData;
19900     },
19901
19902     // private
19903     onDragEnter : function(e, id){
19904         var target = Roo.dd.DragDropMgr.getDDById(id);
19905         this.cachedTarget = target;
19906         if(this.beforeDragEnter(target, e, id) !== false){
19907             if(target.isNotifyTarget){
19908                 var status = target.notifyEnter(this, e, this.dragData);
19909                 this.proxy.setStatus(status);
19910             }else{
19911                 this.proxy.setStatus(this.dropAllowed);
19912             }
19913             
19914             if(this.afterDragEnter){
19915                 /**
19916                  * An empty function by default, but provided so that you can perform a custom action
19917                  * when the dragged item enters the drop target by providing an implementation.
19918                  * @param {Roo.dd.DragDrop} target The drop target
19919                  * @param {Event} e The event object
19920                  * @param {String} id The id of the dragged element
19921                  * @method afterDragEnter
19922                  */
19923                 this.afterDragEnter(target, e, id);
19924             }
19925         }
19926     },
19927
19928     /**
19929      * An empty function by default, but provided so that you can perform a custom action
19930      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19931      * @param {Roo.dd.DragDrop} target The drop target
19932      * @param {Event} e The event object
19933      * @param {String} id The id of the dragged element
19934      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19935      */
19936     beforeDragEnter : function(target, e, id){
19937         return true;
19938     },
19939
19940     // private
19941     alignElWithMouse: function() {
19942         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19943         this.proxy.sync();
19944     },
19945
19946     // private
19947     onDragOver : function(e, id){
19948         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19949         if(this.beforeDragOver(target, e, id) !== false){
19950             if(target.isNotifyTarget){
19951                 var status = target.notifyOver(this, e, this.dragData);
19952                 this.proxy.setStatus(status);
19953             }
19954
19955             if(this.afterDragOver){
19956                 /**
19957                  * An empty function by default, but provided so that you can perform a custom action
19958                  * while the dragged item is over the drop target by providing an implementation.
19959                  * @param {Roo.dd.DragDrop} target The drop target
19960                  * @param {Event} e The event object
19961                  * @param {String} id The id of the dragged element
19962                  * @method afterDragOver
19963                  */
19964                 this.afterDragOver(target, e, id);
19965             }
19966         }
19967     },
19968
19969     /**
19970      * An empty function by default, but provided so that you can perform a custom action
19971      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19972      * @param {Roo.dd.DragDrop} target The drop target
19973      * @param {Event} e The event object
19974      * @param {String} id The id of the dragged element
19975      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19976      */
19977     beforeDragOver : function(target, e, id){
19978         return true;
19979     },
19980
19981     // private
19982     onDragOut : function(e, id){
19983         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19984         if(this.beforeDragOut(target, e, id) !== false){
19985             if(target.isNotifyTarget){
19986                 target.notifyOut(this, e, this.dragData);
19987             }
19988             this.proxy.reset();
19989             if(this.afterDragOut){
19990                 /**
19991                  * An empty function by default, but provided so that you can perform a custom action
19992                  * after the dragged item is dragged out of the target without dropping.
19993                  * @param {Roo.dd.DragDrop} target The drop target
19994                  * @param {Event} e The event object
19995                  * @param {String} id The id of the dragged element
19996                  * @method afterDragOut
19997                  */
19998                 this.afterDragOut(target, e, id);
19999             }
20000         }
20001         this.cachedTarget = null;
20002     },
20003
20004     /**
20005      * An empty function by default, but provided so that you can perform a custom action before the dragged
20006      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
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      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20011      */
20012     beforeDragOut : function(target, e, id){
20013         return true;
20014     },
20015     
20016     // private
20017     onDragDrop : function(e, id){
20018         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20019         if(this.beforeDragDrop(target, e, id) !== false){
20020             if(target.isNotifyTarget){
20021                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20022                     this.onValidDrop(target, e, id);
20023                 }else{
20024                     this.onInvalidDrop(target, e, id);
20025                 }
20026             }else{
20027                 this.onValidDrop(target, e, id);
20028             }
20029             
20030             if(this.afterDragDrop){
20031                 /**
20032                  * An empty function by default, but provided so that you can perform a custom action
20033                  * after a valid drag drop has occurred by providing an implementation.
20034                  * @param {Roo.dd.DragDrop} target The drop target
20035                  * @param {Event} e The event object
20036                  * @param {String} id The id of the dropped element
20037                  * @method afterDragDrop
20038                  */
20039                 this.afterDragDrop(target, e, id);
20040             }
20041         }
20042         delete this.cachedTarget;
20043     },
20044
20045     /**
20046      * An empty function by default, but provided so that you can perform a custom action before the dragged
20047      * item is dropped onto the target and optionally cancel the onDragDrop.
20048      * @param {Roo.dd.DragDrop} target The drop target
20049      * @param {Event} e The event object
20050      * @param {String} id The id of the dragged element
20051      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20052      */
20053     beforeDragDrop : function(target, e, id){
20054         return true;
20055     },
20056
20057     // private
20058     onValidDrop : function(target, e, id){
20059         this.hideProxy();
20060         if(this.afterValidDrop){
20061             /**
20062              * An empty function by default, but provided so that you can perform a custom action
20063              * after a valid drop has occurred by providing an implementation.
20064              * @param {Object} target The target DD 
20065              * @param {Event} e The event object
20066              * @param {String} id The id of the dropped element
20067              * @method afterInvalidDrop
20068              */
20069             this.afterValidDrop(target, e, id);
20070         }
20071     },
20072
20073     // private
20074     getRepairXY : function(e, data){
20075         return this.el.getXY();  
20076     },
20077
20078     // private
20079     onInvalidDrop : function(target, e, id){
20080         this.beforeInvalidDrop(target, e, id);
20081         if(this.cachedTarget){
20082             if(this.cachedTarget.isNotifyTarget){
20083                 this.cachedTarget.notifyOut(this, e, this.dragData);
20084             }
20085             this.cacheTarget = null;
20086         }
20087         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20088
20089         if(this.afterInvalidDrop){
20090             /**
20091              * An empty function by default, but provided so that you can perform a custom action
20092              * after an invalid drop has occurred by providing an implementation.
20093              * @param {Event} e The event object
20094              * @param {String} id The id of the dropped element
20095              * @method afterInvalidDrop
20096              */
20097             this.afterInvalidDrop(e, id);
20098         }
20099     },
20100
20101     // private
20102     afterRepair : function(){
20103         if(Roo.enableFx){
20104             this.el.highlight(this.hlColor || "c3daf9");
20105         }
20106         this.dragging = false;
20107     },
20108
20109     /**
20110      * An empty function by default, but provided so that you can perform a custom action after an invalid
20111      * drop has occurred.
20112      * @param {Roo.dd.DragDrop} target The drop target
20113      * @param {Event} e The event object
20114      * @param {String} id The id of the dragged element
20115      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20116      */
20117     beforeInvalidDrop : function(target, e, id){
20118         return true;
20119     },
20120
20121     // private
20122     handleMouseDown : function(e){
20123         if(this.dragging) {
20124             return;
20125         }
20126         var data = this.getDragData(e);
20127         if(data && this.onBeforeDrag(data, e) !== false){
20128             this.dragData = data;
20129             this.proxy.stop();
20130             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20131         } 
20132     },
20133
20134     /**
20135      * An empty function by default, but provided so that you can perform a custom action before the initial
20136      * drag event begins and optionally cancel it.
20137      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20138      * @param {Event} e The event object
20139      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20140      */
20141     onBeforeDrag : function(data, e){
20142         return true;
20143     },
20144
20145     /**
20146      * An empty function by default, but provided so that you can perform a custom action once the initial
20147      * drag event has begun.  The drag cannot be canceled from this function.
20148      * @param {Number} x The x position of the click on the dragged object
20149      * @param {Number} y The y position of the click on the dragged object
20150      */
20151     onStartDrag : Roo.emptyFn,
20152
20153     // private - YUI override
20154     startDrag : function(x, y){
20155         this.proxy.reset();
20156         this.dragging = true;
20157         this.proxy.update("");
20158         this.onInitDrag(x, y);
20159         this.proxy.show();
20160     },
20161
20162     // private
20163     onInitDrag : function(x, y){
20164         var clone = this.el.dom.cloneNode(true);
20165         clone.id = Roo.id(); // prevent duplicate ids
20166         this.proxy.update(clone);
20167         this.onStartDrag(x, y);
20168         return true;
20169     },
20170
20171     /**
20172      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20173      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20174      */
20175     getProxy : function(){
20176         return this.proxy;  
20177     },
20178
20179     /**
20180      * Hides the drag source's {@link Roo.dd.StatusProxy}
20181      */
20182     hideProxy : function(){
20183         this.proxy.hide();  
20184         this.proxy.reset(true);
20185         this.dragging = false;
20186     },
20187
20188     // private
20189     triggerCacheRefresh : function(){
20190         Roo.dd.DDM.refreshCache(this.groups);
20191     },
20192
20193     // private - override to prevent hiding
20194     b4EndDrag: function(e) {
20195     },
20196
20197     // private - override to prevent moving
20198     endDrag : function(e){
20199         this.onEndDrag(this.dragData, e);
20200     },
20201
20202     // private
20203     onEndDrag : function(data, e){
20204     },
20205     
20206     // private - pin to cursor
20207     autoOffset : function(x, y) {
20208         this.setDelta(-12, -20);
20209     }    
20210 });/*
20211  * Based on:
20212  * Ext JS Library 1.1.1
20213  * Copyright(c) 2006-2007, Ext JS, LLC.
20214  *
20215  * Originally Released Under LGPL - original licence link has changed is not relivant.
20216  *
20217  * Fork - LGPL
20218  * <script type="text/javascript">
20219  */
20220
20221
20222 /**
20223  * @class Roo.dd.DropTarget
20224  * @extends Roo.dd.DDTarget
20225  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20226  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20227  * @constructor
20228  * @param {String/HTMLElement/Element} el The container element
20229  * @param {Object} config
20230  */
20231 Roo.dd.DropTarget = function(el, config){
20232     this.el = Roo.get(el);
20233     
20234     var listeners = false; ;
20235     if (config && config.listeners) {
20236         listeners= config.listeners;
20237         delete config.listeners;
20238     }
20239     Roo.apply(this, config);
20240     
20241     if(this.containerScroll){
20242         Roo.dd.ScrollManager.register(this.el);
20243     }
20244     this.addEvents( {
20245          /**
20246          * @scope Roo.dd.DropTarget
20247          */
20248          
20249          /**
20250          * @event enter
20251          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20252          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20253          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20254          * 
20255          * IMPORTANT : it should set this.overClass and this.dropAllowed
20256          * 
20257          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20258          * @param {Event} e The event
20259          * @param {Object} data An object containing arbitrary data supplied by the drag source
20260          */
20261         "enter" : true,
20262         
20263          /**
20264          * @event over
20265          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20266          * This method will be called on every mouse movement while the drag source is over the drop target.
20267          * This default implementation simply returns the dropAllowed config value.
20268          * 
20269          * IMPORTANT : it should set 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          */
20276         "over" : true,
20277         /**
20278          * @event out
20279          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20280          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20281          * overClass (if any) from the drop element.
20282          * 
20283          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20284          * @param {Event} e The event
20285          * @param {Object} data An object containing arbitrary data supplied by the drag source
20286          */
20287          "out" : true,
20288          
20289         /**
20290          * @event drop
20291          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20292          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20293          * implementation that does something to process the drop event and returns true so that the drag source's
20294          * repair action does not run.
20295          * 
20296          * IMPORTANT : it should set this.success
20297          * 
20298          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20299          * @param {Event} e The event
20300          * @param {Object} data An object containing arbitrary data supplied by the drag source
20301         */
20302          "drop" : true
20303     });
20304             
20305      
20306     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20307         this.el.dom, 
20308         this.ddGroup || this.group,
20309         {
20310             isTarget: true,
20311             listeners : listeners || {} 
20312            
20313         
20314         }
20315     );
20316
20317 };
20318
20319 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20320     /**
20321      * @cfg {String} overClass
20322      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20323      */
20324      /**
20325      * @cfg {String} ddGroup
20326      * The drag drop group to handle drop events for
20327      */
20328      
20329     /**
20330      * @cfg {String} dropAllowed
20331      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20332      */
20333     dropAllowed : "x-dd-drop-ok",
20334     /**
20335      * @cfg {String} dropNotAllowed
20336      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20337      */
20338     dropNotAllowed : "x-dd-drop-nodrop",
20339     /**
20340      * @cfg {boolean} success
20341      * set this after drop listener.. 
20342      */
20343     success : false,
20344     /**
20345      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20346      * if the drop point is valid for over/enter..
20347      */
20348     valid : false,
20349     // private
20350     isTarget : true,
20351
20352     // private
20353     isNotifyTarget : true,
20354     
20355     /**
20356      * @hide
20357      */
20358     notifyEnter : function(dd, e, data)
20359     {
20360         this.valid = true;
20361         this.fireEvent('enter', dd, e, data);
20362         if(this.overClass){
20363             this.el.addClass(this.overClass);
20364         }
20365         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20366             this.valid ? this.dropAllowed : this.dropNotAllowed
20367         );
20368     },
20369
20370     /**
20371      * @hide
20372      */
20373     notifyOver : function(dd, e, data)
20374     {
20375         this.valid = true;
20376         this.fireEvent('over', dd, e, data);
20377         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20378             this.valid ? this.dropAllowed : this.dropNotAllowed
20379         );
20380     },
20381
20382     /**
20383      * @hide
20384      */
20385     notifyOut : function(dd, e, data)
20386     {
20387         this.fireEvent('out', dd, e, data);
20388         if(this.overClass){
20389             this.el.removeClass(this.overClass);
20390         }
20391     },
20392
20393     /**
20394      * @hide
20395      */
20396     notifyDrop : function(dd, e, data)
20397     {
20398         this.success = false;
20399         this.fireEvent('drop', dd, e, data);
20400         return this.success;
20401     }
20402 });/*
20403  * Based on:
20404  * Ext JS Library 1.1.1
20405  * Copyright(c) 2006-2007, Ext JS, LLC.
20406  *
20407  * Originally Released Under LGPL - original licence link has changed is not relivant.
20408  *
20409  * Fork - LGPL
20410  * <script type="text/javascript">
20411  */
20412
20413
20414 /**
20415  * @class Roo.dd.DragZone
20416  * @extends Roo.dd.DragSource
20417  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20418  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20419  * @constructor
20420  * @param {String/HTMLElement/Element} el The container element
20421  * @param {Object} config
20422  */
20423 Roo.dd.DragZone = function(el, config){
20424     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20425     if(this.containerScroll){
20426         Roo.dd.ScrollManager.register(this.el);
20427     }
20428 };
20429
20430 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20431     /**
20432      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20433      * for auto scrolling during drag operations.
20434      */
20435     /**
20436      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20437      * method after a failed drop (defaults to "c3daf9" - light blue)
20438      */
20439
20440     /**
20441      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20442      * for a valid target to drag based on the mouse down. Override this method
20443      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20444      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20445      * @param {EventObject} e The mouse down event
20446      * @return {Object} The dragData
20447      */
20448     getDragData : function(e){
20449         return Roo.dd.Registry.getHandleFromEvent(e);
20450     },
20451     
20452     /**
20453      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20454      * this.dragData.ddel
20455      * @param {Number} x The x position of the click on the dragged object
20456      * @param {Number} y The y position of the click on the dragged object
20457      * @return {Boolean} true to continue the drag, false to cancel
20458      */
20459     onInitDrag : function(x, y){
20460         this.proxy.update(this.dragData.ddel.cloneNode(true));
20461         this.onStartDrag(x, y);
20462         return true;
20463     },
20464     
20465     /**
20466      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20467      */
20468     afterRepair : function(){
20469         if(Roo.enableFx){
20470             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20471         }
20472         this.dragging = false;
20473     },
20474
20475     /**
20476      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20477      * the XY of this.dragData.ddel
20478      * @param {EventObject} e The mouse up event
20479      * @return {Array} The xy location (e.g. [100, 200])
20480      */
20481     getRepairXY : function(e){
20482         return Roo.Element.fly(this.dragData.ddel).getXY();  
20483     }
20484 });/*
20485  * Based on:
20486  * Ext JS Library 1.1.1
20487  * Copyright(c) 2006-2007, Ext JS, LLC.
20488  *
20489  * Originally Released Under LGPL - original licence link has changed is not relivant.
20490  *
20491  * Fork - LGPL
20492  * <script type="text/javascript">
20493  */
20494 /**
20495  * @class Roo.dd.DropZone
20496  * @extends Roo.dd.DropTarget
20497  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20498  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20499  * @constructor
20500  * @param {String/HTMLElement/Element} el The container element
20501  * @param {Object} config
20502  */
20503 Roo.dd.DropZone = function(el, config){
20504     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20505 };
20506
20507 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20508     /**
20509      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20510      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20511      * provide your own custom lookup.
20512      * @param {Event} e The event
20513      * @return {Object} data The custom data
20514      */
20515     getTargetFromEvent : function(e){
20516         return Roo.dd.Registry.getTargetFromEvent(e);
20517     },
20518
20519     /**
20520      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20521      * that it has registered.  This method has no default implementation and should be overridden to provide
20522      * node-specific processing if necessary.
20523      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20524      * {@link #getTargetFromEvent} for this node)
20525      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20526      * @param {Event} e The event
20527      * @param {Object} data An object containing arbitrary data supplied by the drag source
20528      */
20529     onNodeEnter : function(n, dd, e, data){
20530         
20531     },
20532
20533     /**
20534      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20535      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20536      * overridden to provide the proper feedback.
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      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20543      * underlying {@link Roo.dd.StatusProxy} can be updated
20544      */
20545     onNodeOver : function(n, dd, e, data){
20546         return this.dropAllowed;
20547     },
20548
20549     /**
20550      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20551      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20552      * node-specific processing if necessary.
20553      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20554      * {@link #getTargetFromEvent} for this node)
20555      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20556      * @param {Event} e The event
20557      * @param {Object} data An object containing arbitrary data supplied by the drag source
20558      */
20559     onNodeOut : function(n, dd, e, data){
20560         
20561     },
20562
20563     /**
20564      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20565      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20566      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
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      * @return {Boolean} True if the drop was valid, else false
20573      */
20574     onNodeDrop : function(n, dd, e, data){
20575         return false;
20576     },
20577
20578     /**
20579      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20580      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20581      * it should be overridden to provide the proper feedback if necessary.
20582      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20583      * @param {Event} e The event
20584      * @param {Object} data An object containing arbitrary data supplied by the drag source
20585      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20586      * underlying {@link Roo.dd.StatusProxy} can be updated
20587      */
20588     onContainerOver : function(dd, e, data){
20589         return this.dropNotAllowed;
20590     },
20591
20592     /**
20593      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20594      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20595      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20596      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20597      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20598      * @param {Event} e The event
20599      * @param {Object} data An object containing arbitrary data supplied by the drag source
20600      * @return {Boolean} True if the drop was valid, else false
20601      */
20602     onContainerDrop : function(dd, e, data){
20603         return false;
20604     },
20605
20606     /**
20607      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20608      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20609      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20610      * you should override this method and provide a custom implementation.
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 {String} status The CSS class that communicates the drop status back to the source so that the
20615      * underlying {@link Roo.dd.StatusProxy} can be updated
20616      */
20617     notifyEnter : function(dd, e, data){
20618         return this.dropNotAllowed;
20619     },
20620
20621     /**
20622      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20623      * This method will be called on every mouse movement while the drag source is over the drop zone.
20624      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20625      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20626      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20627      * registered node, it will call {@link #onContainerOver}.
20628      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20629      * @param {Event} e The event
20630      * @param {Object} data An object containing arbitrary data supplied by the drag source
20631      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20632      * underlying {@link Roo.dd.StatusProxy} can be updated
20633      */
20634     notifyOver : function(dd, e, data){
20635         var n = this.getTargetFromEvent(e);
20636         if(!n){ // not over valid drop target
20637             if(this.lastOverNode){
20638                 this.onNodeOut(this.lastOverNode, dd, e, data);
20639                 this.lastOverNode = null;
20640             }
20641             return this.onContainerOver(dd, e, data);
20642         }
20643         if(this.lastOverNode != n){
20644             if(this.lastOverNode){
20645                 this.onNodeOut(this.lastOverNode, dd, e, data);
20646             }
20647             this.onNodeEnter(n, dd, e, data);
20648             this.lastOverNode = n;
20649         }
20650         return this.onNodeOver(n, dd, e, data);
20651     },
20652
20653     /**
20654      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20655      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20656      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20657      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20658      * @param {Event} e The event
20659      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20660      */
20661     notifyOut : function(dd, e, data){
20662         if(this.lastOverNode){
20663             this.onNodeOut(this.lastOverNode, dd, e, data);
20664             this.lastOverNode = null;
20665         }
20666     },
20667
20668     /**
20669      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20670      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20671      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20672      * otherwise it will call {@link #onContainerDrop}.
20673      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20674      * @param {Event} e The event
20675      * @param {Object} data An object containing arbitrary data supplied by the drag source
20676      * @return {Boolean} True if the drop was valid, else false
20677      */
20678     notifyDrop : function(dd, e, data){
20679         if(this.lastOverNode){
20680             this.onNodeOut(this.lastOverNode, dd, e, data);
20681             this.lastOverNode = null;
20682         }
20683         var n = this.getTargetFromEvent(e);
20684         return n ?
20685             this.onNodeDrop(n, dd, e, data) :
20686             this.onContainerDrop(dd, e, data);
20687     },
20688
20689     // private
20690     triggerCacheRefresh : function(){
20691         Roo.dd.DDM.refreshCache(this.groups);
20692     }  
20693 });/*
20694  * Based on:
20695  * Ext JS Library 1.1.1
20696  * Copyright(c) 2006-2007, Ext JS, LLC.
20697  *
20698  * Originally Released Under LGPL - original licence link has changed is not relivant.
20699  *
20700  * Fork - LGPL
20701  * <script type="text/javascript">
20702  */
20703
20704
20705 /**
20706  * @class Roo.data.SortTypes
20707  * @singleton
20708  * Defines the default sorting (casting?) comparison functions used when sorting data.
20709  */
20710 Roo.data.SortTypes = {
20711     /**
20712      * Default sort that does nothing
20713      * @param {Mixed} s The value being converted
20714      * @return {Mixed} The comparison value
20715      */
20716     none : function(s){
20717         return s;
20718     },
20719     
20720     /**
20721      * The regular expression used to strip tags
20722      * @type {RegExp}
20723      * @property
20724      */
20725     stripTagsRE : /<\/?[^>]+>/gi,
20726     
20727     /**
20728      * Strips all HTML tags to sort on text only
20729      * @param {Mixed} s The value being converted
20730      * @return {String} The comparison value
20731      */
20732     asText : function(s){
20733         return String(s).replace(this.stripTagsRE, "");
20734     },
20735     
20736     /**
20737      * Strips all HTML tags to sort on text only - Case insensitive
20738      * @param {Mixed} s The value being converted
20739      * @return {String} The comparison value
20740      */
20741     asUCText : function(s){
20742         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20743     },
20744     
20745     /**
20746      * Case insensitive string
20747      * @param {Mixed} s The value being converted
20748      * @return {String} The comparison value
20749      */
20750     asUCString : function(s) {
20751         return String(s).toUpperCase();
20752     },
20753     
20754     /**
20755      * Date sorting
20756      * @param {Mixed} s The value being converted
20757      * @return {Number} The comparison value
20758      */
20759     asDate : function(s) {
20760         if(!s){
20761             return 0;
20762         }
20763         if(s instanceof Date){
20764             return s.getTime();
20765         }
20766         return Date.parse(String(s));
20767     },
20768     
20769     /**
20770      * Float sorting
20771      * @param {Mixed} s The value being converted
20772      * @return {Float} The comparison value
20773      */
20774     asFloat : function(s) {
20775         var val = parseFloat(String(s).replace(/,/g, ""));
20776         if(isNaN(val)) val = 0;
20777         return val;
20778     },
20779     
20780     /**
20781      * Integer sorting
20782      * @param {Mixed} s The value being converted
20783      * @return {Number} The comparison value
20784      */
20785     asInt : function(s) {
20786         var val = parseInt(String(s).replace(/,/g, ""));
20787         if(isNaN(val)) val = 0;
20788         return val;
20789     }
20790 };/*
20791  * Based on:
20792  * Ext JS Library 1.1.1
20793  * Copyright(c) 2006-2007, Ext JS, LLC.
20794  *
20795  * Originally Released Under LGPL - original licence link has changed is not relivant.
20796  *
20797  * Fork - LGPL
20798  * <script type="text/javascript">
20799  */
20800
20801 /**
20802 * @class Roo.data.Record
20803  * Instances of this class encapsulate both record <em>definition</em> information, and record
20804  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20805  * to access Records cached in an {@link Roo.data.Store} object.<br>
20806  * <p>
20807  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20808  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20809  * objects.<br>
20810  * <p>
20811  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20812  * @constructor
20813  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20814  * {@link #create}. The parameters are the same.
20815  * @param {Array} data An associative Array of data values keyed by the field name.
20816  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20817  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20818  * not specified an integer id is generated.
20819  */
20820 Roo.data.Record = function(data, id){
20821     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20822     this.data = data;
20823 };
20824
20825 /**
20826  * Generate a constructor for a specific record layout.
20827  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20828  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20829  * Each field definition object may contain the following properties: <ul>
20830  * <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,
20831  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20832  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20833  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20834  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20835  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20836  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20837  * this may be omitted.</p></li>
20838  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20839  * <ul><li>auto (Default, implies no conversion)</li>
20840  * <li>string</li>
20841  * <li>int</li>
20842  * <li>float</li>
20843  * <li>boolean</li>
20844  * <li>date</li></ul></p></li>
20845  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20846  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20847  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20848  * by the Reader into an object that will be stored in the Record. It is passed the
20849  * following parameters:<ul>
20850  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20851  * </ul></p></li>
20852  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20853  * </ul>
20854  * <br>usage:<br><pre><code>
20855 var TopicRecord = Roo.data.Record.create(
20856     {name: 'title', mapping: 'topic_title'},
20857     {name: 'author', mapping: 'username'},
20858     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20859     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20860     {name: 'lastPoster', mapping: 'user2'},
20861     {name: 'excerpt', mapping: 'post_text'}
20862 );
20863
20864 var myNewRecord = new TopicRecord({
20865     title: 'Do my job please',
20866     author: 'noobie',
20867     totalPosts: 1,
20868     lastPost: new Date(),
20869     lastPoster: 'Animal',
20870     excerpt: 'No way dude!'
20871 });
20872 myStore.add(myNewRecord);
20873 </code></pre>
20874  * @method create
20875  * @static
20876  */
20877 Roo.data.Record.create = function(o){
20878     var f = function(){
20879         f.superclass.constructor.apply(this, arguments);
20880     };
20881     Roo.extend(f, Roo.data.Record);
20882     var p = f.prototype;
20883     p.fields = new Roo.util.MixedCollection(false, function(field){
20884         return field.name;
20885     });
20886     for(var i = 0, len = o.length; i < len; i++){
20887         p.fields.add(new Roo.data.Field(o[i]));
20888     }
20889     f.getField = function(name){
20890         return p.fields.get(name);  
20891     };
20892     return f;
20893 };
20894
20895 Roo.data.Record.AUTO_ID = 1000;
20896 Roo.data.Record.EDIT = 'edit';
20897 Roo.data.Record.REJECT = 'reject';
20898 Roo.data.Record.COMMIT = 'commit';
20899
20900 Roo.data.Record.prototype = {
20901     /**
20902      * Readonly flag - true if this record has been modified.
20903      * @type Boolean
20904      */
20905     dirty : false,
20906     editing : false,
20907     error: null,
20908     modified: null,
20909
20910     // private
20911     join : function(store){
20912         this.store = store;
20913     },
20914
20915     /**
20916      * Set the named field to the specified value.
20917      * @param {String} name The name of the field to set.
20918      * @param {Object} value The value to set the field to.
20919      */
20920     set : function(name, value){
20921         if(this.data[name] == value){
20922             return;
20923         }
20924         this.dirty = true;
20925         if(!this.modified){
20926             this.modified = {};
20927         }
20928         if(typeof this.modified[name] == 'undefined'){
20929             this.modified[name] = this.data[name];
20930         }
20931         this.data[name] = value;
20932         if(!this.editing && this.store){
20933             this.store.afterEdit(this);
20934         }       
20935     },
20936
20937     /**
20938      * Get the value of the named field.
20939      * @param {String} name The name of the field to get the value of.
20940      * @return {Object} The value of the field.
20941      */
20942     get : function(name){
20943         return this.data[name]; 
20944     },
20945
20946     // private
20947     beginEdit : function(){
20948         this.editing = true;
20949         this.modified = {}; 
20950     },
20951
20952     // private
20953     cancelEdit : function(){
20954         this.editing = false;
20955         delete this.modified;
20956     },
20957
20958     // private
20959     endEdit : function(){
20960         this.editing = false;
20961         if(this.dirty && this.store){
20962             this.store.afterEdit(this);
20963         }
20964     },
20965
20966     /**
20967      * Usually called by the {@link Roo.data.Store} which owns the Record.
20968      * Rejects all changes made to the Record since either creation, or the last commit operation.
20969      * Modified fields are reverted to their original values.
20970      * <p>
20971      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20972      * of reject operations.
20973      */
20974     reject : function(){
20975         var m = this.modified;
20976         for(var n in m){
20977             if(typeof m[n] != "function"){
20978                 this.data[n] = m[n];
20979             }
20980         }
20981         this.dirty = false;
20982         delete this.modified;
20983         this.editing = false;
20984         if(this.store){
20985             this.store.afterReject(this);
20986         }
20987     },
20988
20989     /**
20990      * Usually called by the {@link Roo.data.Store} which owns the Record.
20991      * Commits all changes made to the Record since either creation, or the last commit operation.
20992      * <p>
20993      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20994      * of commit operations.
20995      */
20996     commit : function(){
20997         this.dirty = false;
20998         delete this.modified;
20999         this.editing = false;
21000         if(this.store){
21001             this.store.afterCommit(this);
21002         }
21003     },
21004
21005     // private
21006     hasError : function(){
21007         return this.error != null;
21008     },
21009
21010     // private
21011     clearError : function(){
21012         this.error = null;
21013     },
21014
21015     /**
21016      * Creates a copy of this record.
21017      * @param {String} id (optional) A new record id if you don't want to use this record's id
21018      * @return {Record}
21019      */
21020     copy : function(newId) {
21021         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21022     }
21023 };/*
21024  * Based on:
21025  * Ext JS Library 1.1.1
21026  * Copyright(c) 2006-2007, Ext JS, LLC.
21027  *
21028  * Originally Released Under LGPL - original licence link has changed is not relivant.
21029  *
21030  * Fork - LGPL
21031  * <script type="text/javascript">
21032  */
21033
21034
21035
21036 /**
21037  * @class Roo.data.Store
21038  * @extends Roo.util.Observable
21039  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21040  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21041  * <p>
21042  * 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
21043  * has no knowledge of the format of the data returned by the Proxy.<br>
21044  * <p>
21045  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21046  * instances from the data object. These records are cached and made available through accessor functions.
21047  * @constructor
21048  * Creates a new Store.
21049  * @param {Object} config A config object containing the objects needed for the Store to access data,
21050  * and read the data into Records.
21051  */
21052 Roo.data.Store = function(config){
21053     this.data = new Roo.util.MixedCollection(false);
21054     this.data.getKey = function(o){
21055         return o.id;
21056     };
21057     this.baseParams = {};
21058     // private
21059     this.paramNames = {
21060         "start" : "start",
21061         "limit" : "limit",
21062         "sort" : "sort",
21063         "dir" : "dir",
21064         "multisort" : "_multisort"
21065     };
21066
21067     if(config && config.data){
21068         this.inlineData = config.data;
21069         delete config.data;
21070     }
21071
21072     Roo.apply(this, config);
21073     
21074     if(this.reader){ // reader passed
21075         this.reader = Roo.factory(this.reader, Roo.data);
21076         this.reader.xmodule = this.xmodule || false;
21077         if(!this.recordType){
21078             this.recordType = this.reader.recordType;
21079         }
21080         if(this.reader.onMetaChange){
21081             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21082         }
21083     }
21084
21085     if(this.recordType){
21086         this.fields = this.recordType.prototype.fields;
21087     }
21088     this.modified = [];
21089
21090     this.addEvents({
21091         /**
21092          * @event datachanged
21093          * Fires when the data cache has changed, and a widget which is using this Store
21094          * as a Record cache should refresh its view.
21095          * @param {Store} this
21096          */
21097         datachanged : true,
21098         /**
21099          * @event metachange
21100          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21101          * @param {Store} this
21102          * @param {Object} meta The JSON metadata
21103          */
21104         metachange : true,
21105         /**
21106          * @event add
21107          * Fires when Records have been added to the Store
21108          * @param {Store} this
21109          * @param {Roo.data.Record[]} records The array of Records added
21110          * @param {Number} index The index at which the record(s) were added
21111          */
21112         add : true,
21113         /**
21114          * @event remove
21115          * Fires when a Record has been removed from the Store
21116          * @param {Store} this
21117          * @param {Roo.data.Record} record The Record that was removed
21118          * @param {Number} index The index at which the record was removed
21119          */
21120         remove : true,
21121         /**
21122          * @event update
21123          * Fires when a Record has been updated
21124          * @param {Store} this
21125          * @param {Roo.data.Record} record The Record that was updated
21126          * @param {String} operation The update operation being performed.  Value may be one of:
21127          * <pre><code>
21128  Roo.data.Record.EDIT
21129  Roo.data.Record.REJECT
21130  Roo.data.Record.COMMIT
21131          * </code></pre>
21132          */
21133         update : true,
21134         /**
21135          * @event clear
21136          * Fires when the data cache has been cleared.
21137          * @param {Store} this
21138          */
21139         clear : true,
21140         /**
21141          * @event beforeload
21142          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21143          * the load action will be canceled.
21144          * @param {Store} this
21145          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21146          */
21147         beforeload : true,
21148         /**
21149          * @event beforeloadadd
21150          * Fires after a new set of Records has been loaded.
21151          * @param {Store} this
21152          * @param {Roo.data.Record[]} records The Records that were loaded
21153          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21154          */
21155         beforeloadadd : true,
21156         /**
21157          * @event load
21158          * Fires after a new set of Records has been loaded, before they are added to the store.
21159          * @param {Store} this
21160          * @param {Roo.data.Record[]} records The Records that were loaded
21161          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21162          * @params {Object} return from reader
21163          */
21164         load : true,
21165         /**
21166          * @event loadexception
21167          * Fires if an exception occurs in the Proxy during loading.
21168          * Called with the signature of the Proxy's "loadexception" event.
21169          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21170          * 
21171          * @param {Proxy} 
21172          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21173          * @param {Object} load options 
21174          * @param {Object} jsonData from your request (normally this contains the Exception)
21175          */
21176         loadexception : true
21177     });
21178     
21179     if(this.proxy){
21180         this.proxy = Roo.factory(this.proxy, Roo.data);
21181         this.proxy.xmodule = this.xmodule || false;
21182         this.relayEvents(this.proxy,  ["loadexception"]);
21183     }
21184     this.sortToggle = {};
21185     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21186
21187     Roo.data.Store.superclass.constructor.call(this);
21188
21189     if(this.inlineData){
21190         this.loadData(this.inlineData);
21191         delete this.inlineData;
21192     }
21193 };
21194
21195 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21196      /**
21197     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21198     * without a remote query - used by combo/forms at present.
21199     */
21200     
21201     /**
21202     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21203     */
21204     /**
21205     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21206     */
21207     /**
21208     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21209     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21210     */
21211     /**
21212     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21213     * on any HTTP request
21214     */
21215     /**
21216     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21217     */
21218     /**
21219     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21220     */
21221     multiSort: false,
21222     /**
21223     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21224     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21225     */
21226     remoteSort : false,
21227
21228     /**
21229     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21230      * loaded or when a record is removed. (defaults to false).
21231     */
21232     pruneModifiedRecords : false,
21233
21234     // private
21235     lastOptions : null,
21236
21237     /**
21238      * Add Records to the Store and fires the add event.
21239      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21240      */
21241     add : function(records){
21242         records = [].concat(records);
21243         for(var i = 0, len = records.length; i < len; i++){
21244             records[i].join(this);
21245         }
21246         var index = this.data.length;
21247         this.data.addAll(records);
21248         this.fireEvent("add", this, records, index);
21249     },
21250
21251     /**
21252      * Remove a Record from the Store and fires the remove event.
21253      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21254      */
21255     remove : function(record){
21256         var index = this.data.indexOf(record);
21257         this.data.removeAt(index);
21258         if(this.pruneModifiedRecords){
21259             this.modified.remove(record);
21260         }
21261         this.fireEvent("remove", this, record, index);
21262     },
21263
21264     /**
21265      * Remove all Records from the Store and fires the clear event.
21266      */
21267     removeAll : function(){
21268         this.data.clear();
21269         if(this.pruneModifiedRecords){
21270             this.modified = [];
21271         }
21272         this.fireEvent("clear", this);
21273     },
21274
21275     /**
21276      * Inserts Records to the Store at the given index and fires the add event.
21277      * @param {Number} index The start index at which to insert the passed Records.
21278      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21279      */
21280     insert : function(index, records){
21281         records = [].concat(records);
21282         for(var i = 0, len = records.length; i < len; i++){
21283             this.data.insert(index, records[i]);
21284             records[i].join(this);
21285         }
21286         this.fireEvent("add", this, records, index);
21287     },
21288
21289     /**
21290      * Get the index within the cache of the passed Record.
21291      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21292      * @return {Number} The index of the passed Record. Returns -1 if not found.
21293      */
21294     indexOf : function(record){
21295         return this.data.indexOf(record);
21296     },
21297
21298     /**
21299      * Get the index within the cache of the Record with the passed id.
21300      * @param {String} id The id of the Record to find.
21301      * @return {Number} The index of the Record. Returns -1 if not found.
21302      */
21303     indexOfId : function(id){
21304         return this.data.indexOfKey(id);
21305     },
21306
21307     /**
21308      * Get the Record with the specified id.
21309      * @param {String} id The id of the Record to find.
21310      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21311      */
21312     getById : function(id){
21313         return this.data.key(id);
21314     },
21315
21316     /**
21317      * Get the Record at the specified index.
21318      * @param {Number} index The index of the Record to find.
21319      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21320      */
21321     getAt : function(index){
21322         return this.data.itemAt(index);
21323     },
21324
21325     /**
21326      * Returns a range of Records between specified indices.
21327      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21328      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21329      * @return {Roo.data.Record[]} An array of Records
21330      */
21331     getRange : function(start, end){
21332         return this.data.getRange(start, end);
21333     },
21334
21335     // private
21336     storeOptions : function(o){
21337         o = Roo.apply({}, o);
21338         delete o.callback;
21339         delete o.scope;
21340         this.lastOptions = o;
21341     },
21342
21343     /**
21344      * Loads the Record cache from the configured Proxy using the configured Reader.
21345      * <p>
21346      * If using remote paging, then the first load call must specify the <em>start</em>
21347      * and <em>limit</em> properties in the options.params property to establish the initial
21348      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21349      * <p>
21350      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21351      * and this call will return before the new data has been loaded. Perform any post-processing
21352      * in a callback function, or in a "load" event handler.</strong>
21353      * <p>
21354      * @param {Object} options An object containing properties which control loading options:<ul>
21355      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21356      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21357      * passed the following arguments:<ul>
21358      * <li>r : Roo.data.Record[]</li>
21359      * <li>options: Options object from the load call</li>
21360      * <li>success: Boolean success indicator</li></ul></li>
21361      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21362      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21363      * </ul>
21364      */
21365     load : function(options){
21366         options = options || {};
21367         if(this.fireEvent("beforeload", this, options) !== false){
21368             this.storeOptions(options);
21369             var p = Roo.apply(options.params || {}, this.baseParams);
21370             // if meta was not loaded from remote source.. try requesting it.
21371             if (!this.reader.metaFromRemote) {
21372                 p._requestMeta = 1;
21373             }
21374             if(this.sortInfo && this.remoteSort){
21375                 var pn = this.paramNames;
21376                 p[pn["sort"]] = this.sortInfo.field;
21377                 p[pn["dir"]] = this.sortInfo.direction;
21378             }
21379             if (this.multiSort) {
21380                 var pn = this.paramNames;
21381                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21382             }
21383             
21384             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21385         }
21386     },
21387
21388     /**
21389      * Reloads the Record cache from the configured Proxy using the configured Reader and
21390      * the options from the last load operation performed.
21391      * @param {Object} options (optional) An object containing properties which may override the options
21392      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21393      * the most recently used options are reused).
21394      */
21395     reload : function(options){
21396         this.load(Roo.applyIf(options||{}, this.lastOptions));
21397     },
21398
21399     // private
21400     // Called as a callback by the Reader during a load operation.
21401     loadRecords : function(o, options, success){
21402         if(!o || success === false){
21403             if(success !== false){
21404                 this.fireEvent("load", this, [], options, o);
21405             }
21406             if(options.callback){
21407                 options.callback.call(options.scope || this, [], options, false);
21408             }
21409             return;
21410         }
21411         // if data returned failure - throw an exception.
21412         if (o.success === false) {
21413             // show a message if no listener is registered.
21414             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21415                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21416             }
21417             // loadmask wil be hooked into this..
21418             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21419             return;
21420         }
21421         var r = o.records, t = o.totalRecords || r.length;
21422         
21423         this.fireEvent("beforeloadadd", this, r, options, o);
21424         
21425         if(!options || options.add !== true){
21426             if(this.pruneModifiedRecords){
21427                 this.modified = [];
21428             }
21429             for(var i = 0, len = r.length; i < len; i++){
21430                 r[i].join(this);
21431             }
21432             if(this.snapshot){
21433                 this.data = this.snapshot;
21434                 delete this.snapshot;
21435             }
21436             this.data.clear();
21437             this.data.addAll(r);
21438             this.totalLength = t;
21439             this.applySort();
21440             this.fireEvent("datachanged", this);
21441         }else{
21442             this.totalLength = Math.max(t, this.data.length+r.length);
21443             this.add(r);
21444         }
21445         this.fireEvent("load", this, r, options, o);
21446         if(options.callback){
21447             options.callback.call(options.scope || this, r, options, true);
21448         }
21449     },
21450
21451
21452     /**
21453      * Loads data from a passed data block. A Reader which understands the format of the data
21454      * must have been configured in the constructor.
21455      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21456      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21457      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21458      */
21459     loadData : function(o, append){
21460         var r = this.reader.readRecords(o);
21461         this.loadRecords(r, {add: append}, true);
21462     },
21463
21464     /**
21465      * Gets the number of cached records.
21466      * <p>
21467      * <em>If using paging, this may not be the total size of the dataset. If the data object
21468      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21469      * the data set size</em>
21470      */
21471     getCount : function(){
21472         return this.data.length || 0;
21473     },
21474
21475     /**
21476      * Gets the total number of records in the dataset as returned by the server.
21477      * <p>
21478      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21479      * the dataset size</em>
21480      */
21481     getTotalCount : function(){
21482         return this.totalLength || 0;
21483     },
21484
21485     /**
21486      * Returns the sort state of the Store as an object with two properties:
21487      * <pre><code>
21488  field {String} The name of the field by which the Records are sorted
21489  direction {String} The sort order, "ASC" or "DESC"
21490      * </code></pre>
21491      */
21492     getSortState : function(){
21493         return this.sortInfo;
21494     },
21495
21496     // private
21497     applySort : function(){
21498         if(this.sortInfo && !this.remoteSort){
21499             var s = this.sortInfo, f = s.field;
21500             var st = this.fields.get(f).sortType;
21501             var fn = function(r1, r2){
21502                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21503                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21504             };
21505             this.data.sort(s.direction, fn);
21506             if(this.snapshot && this.snapshot != this.data){
21507                 this.snapshot.sort(s.direction, fn);
21508             }
21509         }
21510     },
21511
21512     /**
21513      * Sets the default sort column and order to be used by the next load operation.
21514      * @param {String} fieldName The name of the field to sort by.
21515      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21516      */
21517     setDefaultSort : function(field, dir){
21518         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21519     },
21520
21521     /**
21522      * Sort the Records.
21523      * If remote sorting is used, the sort is performed on the server, and the cache is
21524      * reloaded. If local sorting is used, the cache is sorted internally.
21525      * @param {String} fieldName The name of the field to sort by.
21526      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21527      */
21528     sort : function(fieldName, dir){
21529         var f = this.fields.get(fieldName);
21530         if(!dir){
21531             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21532             
21533             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21534                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21535             }else{
21536                 dir = f.sortDir;
21537             }
21538         }
21539         this.sortToggle[f.name] = dir;
21540         this.sortInfo = {field: f.name, direction: dir};
21541         if(!this.remoteSort){
21542             this.applySort();
21543             this.fireEvent("datachanged", this);
21544         }else{
21545             this.load(this.lastOptions);
21546         }
21547     },
21548
21549     /**
21550      * Calls the specified function for each of the Records in the cache.
21551      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21552      * Returning <em>false</em> aborts and exits the iteration.
21553      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21554      */
21555     each : function(fn, scope){
21556         this.data.each(fn, scope);
21557     },
21558
21559     /**
21560      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21561      * (e.g., during paging).
21562      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21563      */
21564     getModifiedRecords : function(){
21565         return this.modified;
21566     },
21567
21568     // private
21569     createFilterFn : function(property, value, anyMatch){
21570         if(!value.exec){ // not a regex
21571             value = String(value);
21572             if(value.length == 0){
21573                 return false;
21574             }
21575             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21576         }
21577         return function(r){
21578             return value.test(r.data[property]);
21579         };
21580     },
21581
21582     /**
21583      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21584      * @param {String} property A field on your records
21585      * @param {Number} start The record index to start at (defaults to 0)
21586      * @param {Number} end The last record index to include (defaults to length - 1)
21587      * @return {Number} The sum
21588      */
21589     sum : function(property, start, end){
21590         var rs = this.data.items, v = 0;
21591         start = start || 0;
21592         end = (end || end === 0) ? end : rs.length-1;
21593
21594         for(var i = start; i <= end; i++){
21595             v += (rs[i].data[property] || 0);
21596         }
21597         return v;
21598     },
21599
21600     /**
21601      * Filter the records by a specified property.
21602      * @param {String} field A field on your records
21603      * @param {String/RegExp} value Either a string that the field
21604      * should start with or a RegExp to test against the field
21605      * @param {Boolean} anyMatch True to match any part not just the beginning
21606      */
21607     filter : function(property, value, anyMatch){
21608         var fn = this.createFilterFn(property, value, anyMatch);
21609         return fn ? this.filterBy(fn) : this.clearFilter();
21610     },
21611
21612     /**
21613      * Filter by a function. The specified function will be called with each
21614      * record in this data source. If the function returns true the record is included,
21615      * otherwise it is filtered.
21616      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21617      * @param {Object} scope (optional) The scope of the function (defaults to this)
21618      */
21619     filterBy : function(fn, scope){
21620         this.snapshot = this.snapshot || this.data;
21621         this.data = this.queryBy(fn, scope||this);
21622         this.fireEvent("datachanged", this);
21623     },
21624
21625     /**
21626      * Query the records by a specified property.
21627      * @param {String} field A field on your records
21628      * @param {String/RegExp} value Either a string that the field
21629      * should start with or a RegExp to test against the field
21630      * @param {Boolean} anyMatch True to match any part not just the beginning
21631      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21632      */
21633     query : function(property, value, anyMatch){
21634         var fn = this.createFilterFn(property, value, anyMatch);
21635         return fn ? this.queryBy(fn) : this.data.clone();
21636     },
21637
21638     /**
21639      * Query by a function. The specified function will be called with each
21640      * record in this data source. If the function returns true the record is included
21641      * in the results.
21642      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21643      * @param {Object} scope (optional) The scope of the function (defaults to this)
21644       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21645      **/
21646     queryBy : function(fn, scope){
21647         var data = this.snapshot || this.data;
21648         return data.filterBy(fn, scope||this);
21649     },
21650
21651     /**
21652      * Collects unique values for a particular dataIndex from this store.
21653      * @param {String} dataIndex The property to collect
21654      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21655      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21656      * @return {Array} An array of the unique values
21657      **/
21658     collect : function(dataIndex, allowNull, bypassFilter){
21659         var d = (bypassFilter === true && this.snapshot) ?
21660                 this.snapshot.items : this.data.items;
21661         var v, sv, r = [], l = {};
21662         for(var i = 0, len = d.length; i < len; i++){
21663             v = d[i].data[dataIndex];
21664             sv = String(v);
21665             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21666                 l[sv] = true;
21667                 r[r.length] = v;
21668             }
21669         }
21670         return r;
21671     },
21672
21673     /**
21674      * Revert to a view of the Record cache with no filtering applied.
21675      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21676      */
21677     clearFilter : function(suppressEvent){
21678         if(this.snapshot && this.snapshot != this.data){
21679             this.data = this.snapshot;
21680             delete this.snapshot;
21681             if(suppressEvent !== true){
21682                 this.fireEvent("datachanged", this);
21683             }
21684         }
21685     },
21686
21687     // private
21688     afterEdit : function(record){
21689         if(this.modified.indexOf(record) == -1){
21690             this.modified.push(record);
21691         }
21692         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21693     },
21694     
21695     // private
21696     afterReject : function(record){
21697         this.modified.remove(record);
21698         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21699     },
21700
21701     // private
21702     afterCommit : function(record){
21703         this.modified.remove(record);
21704         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21705     },
21706
21707     /**
21708      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21709      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21710      */
21711     commitChanges : function(){
21712         var m = this.modified.slice(0);
21713         this.modified = [];
21714         for(var i = 0, len = m.length; i < len; i++){
21715             m[i].commit();
21716         }
21717     },
21718
21719     /**
21720      * Cancel outstanding changes on all changed records.
21721      */
21722     rejectChanges : function(){
21723         var m = this.modified.slice(0);
21724         this.modified = [];
21725         for(var i = 0, len = m.length; i < len; i++){
21726             m[i].reject();
21727         }
21728     },
21729
21730     onMetaChange : function(meta, rtype, o){
21731         this.recordType = rtype;
21732         this.fields = rtype.prototype.fields;
21733         delete this.snapshot;
21734         this.sortInfo = meta.sortInfo || this.sortInfo;
21735         this.modified = [];
21736         this.fireEvent('metachange', this, this.reader.meta);
21737     }
21738 });/*
21739  * Based on:
21740  * Ext JS Library 1.1.1
21741  * Copyright(c) 2006-2007, Ext JS, LLC.
21742  *
21743  * Originally Released Under LGPL - original licence link has changed is not relivant.
21744  *
21745  * Fork - LGPL
21746  * <script type="text/javascript">
21747  */
21748
21749 /**
21750  * @class Roo.data.SimpleStore
21751  * @extends Roo.data.Store
21752  * Small helper class to make creating Stores from Array data easier.
21753  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21754  * @cfg {Array} fields An array of field definition objects, or field name strings.
21755  * @cfg {Array} data The multi-dimensional array of data
21756  * @constructor
21757  * @param {Object} config
21758  */
21759 Roo.data.SimpleStore = function(config){
21760     Roo.data.SimpleStore.superclass.constructor.call(this, {
21761         isLocal : true,
21762         reader: new Roo.data.ArrayReader({
21763                 id: config.id
21764             },
21765             Roo.data.Record.create(config.fields)
21766         ),
21767         proxy : new Roo.data.MemoryProxy(config.data)
21768     });
21769     this.load();
21770 };
21771 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21772  * Based on:
21773  * Ext JS Library 1.1.1
21774  * Copyright(c) 2006-2007, Ext JS, LLC.
21775  *
21776  * Originally Released Under LGPL - original licence link has changed is not relivant.
21777  *
21778  * Fork - LGPL
21779  * <script type="text/javascript">
21780  */
21781
21782 /**
21783 /**
21784  * @extends Roo.data.Store
21785  * @class Roo.data.JsonStore
21786  * Small helper class to make creating Stores for JSON data easier. <br/>
21787 <pre><code>
21788 var store = new Roo.data.JsonStore({
21789     url: 'get-images.php',
21790     root: 'images',
21791     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21792 });
21793 </code></pre>
21794  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21795  * JsonReader and HttpProxy (unless inline data is provided).</b>
21796  * @cfg {Array} fields An array of field definition objects, or field name strings.
21797  * @constructor
21798  * @param {Object} config
21799  */
21800 Roo.data.JsonStore = function(c){
21801     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21802         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21803         reader: new Roo.data.JsonReader(c, c.fields)
21804     }));
21805 };
21806 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21807  * Based on:
21808  * Ext JS Library 1.1.1
21809  * Copyright(c) 2006-2007, Ext JS, LLC.
21810  *
21811  * Originally Released Under LGPL - original licence link has changed is not relivant.
21812  *
21813  * Fork - LGPL
21814  * <script type="text/javascript">
21815  */
21816
21817  
21818 Roo.data.Field = function(config){
21819     if(typeof config == "string"){
21820         config = {name: config};
21821     }
21822     Roo.apply(this, config);
21823     
21824     if(!this.type){
21825         this.type = "auto";
21826     }
21827     
21828     var st = Roo.data.SortTypes;
21829     // named sortTypes are supported, here we look them up
21830     if(typeof this.sortType == "string"){
21831         this.sortType = st[this.sortType];
21832     }
21833     
21834     // set default sortType for strings and dates
21835     if(!this.sortType){
21836         switch(this.type){
21837             case "string":
21838                 this.sortType = st.asUCString;
21839                 break;
21840             case "date":
21841                 this.sortType = st.asDate;
21842                 break;
21843             default:
21844                 this.sortType = st.none;
21845         }
21846     }
21847
21848     // define once
21849     var stripRe = /[\$,%]/g;
21850
21851     // prebuilt conversion function for this field, instead of
21852     // switching every time we're reading a value
21853     if(!this.convert){
21854         var cv, dateFormat = this.dateFormat;
21855         switch(this.type){
21856             case "":
21857             case "auto":
21858             case undefined:
21859                 cv = function(v){ return v; };
21860                 break;
21861             case "string":
21862                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21863                 break;
21864             case "int":
21865                 cv = function(v){
21866                     return v !== undefined && v !== null && v !== '' ?
21867                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21868                     };
21869                 break;
21870             case "float":
21871                 cv = function(v){
21872                     return v !== undefined && v !== null && v !== '' ?
21873                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21874                     };
21875                 break;
21876             case "bool":
21877             case "boolean":
21878                 cv = function(v){ return v === true || v === "true" || v == 1; };
21879                 break;
21880             case "date":
21881                 cv = function(v){
21882                     if(!v){
21883                         return '';
21884                     }
21885                     if(v instanceof Date){
21886                         return v;
21887                     }
21888                     if(dateFormat){
21889                         if(dateFormat == "timestamp"){
21890                             return new Date(v*1000);
21891                         }
21892                         return Date.parseDate(v, dateFormat);
21893                     }
21894                     var parsed = Date.parse(v);
21895                     return parsed ? new Date(parsed) : null;
21896                 };
21897              break;
21898             
21899         }
21900         this.convert = cv;
21901     }
21902 };
21903
21904 Roo.data.Field.prototype = {
21905     dateFormat: null,
21906     defaultValue: "",
21907     mapping: null,
21908     sortType : null,
21909     sortDir : "ASC"
21910 };/*
21911  * Based on:
21912  * Ext JS Library 1.1.1
21913  * Copyright(c) 2006-2007, Ext JS, LLC.
21914  *
21915  * Originally Released Under LGPL - original licence link has changed is not relivant.
21916  *
21917  * Fork - LGPL
21918  * <script type="text/javascript">
21919  */
21920  
21921 // Base class for reading structured data from a data source.  This class is intended to be
21922 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21923
21924 /**
21925  * @class Roo.data.DataReader
21926  * Base class for reading structured data from a data source.  This class is intended to be
21927  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21928  */
21929
21930 Roo.data.DataReader = function(meta, recordType){
21931     
21932     this.meta = meta;
21933     
21934     this.recordType = recordType instanceof Array ? 
21935         Roo.data.Record.create(recordType) : recordType;
21936 };
21937
21938 Roo.data.DataReader.prototype = {
21939      /**
21940      * Create an empty record
21941      * @param {Object} data (optional) - overlay some values
21942      * @return {Roo.data.Record} record created.
21943      */
21944     newRow :  function(d) {
21945         var da =  {};
21946         this.recordType.prototype.fields.each(function(c) {
21947             switch( c.type) {
21948                 case 'int' : da[c.name] = 0; break;
21949                 case 'date' : da[c.name] = new Date(); break;
21950                 case 'float' : da[c.name] = 0.0; break;
21951                 case 'boolean' : da[c.name] = false; break;
21952                 default : da[c.name] = ""; break;
21953             }
21954             
21955         });
21956         return new this.recordType(Roo.apply(da, d));
21957     }
21958     
21959 };/*
21960  * Based on:
21961  * Ext JS Library 1.1.1
21962  * Copyright(c) 2006-2007, Ext JS, LLC.
21963  *
21964  * Originally Released Under LGPL - original licence link has changed is not relivant.
21965  *
21966  * Fork - LGPL
21967  * <script type="text/javascript">
21968  */
21969
21970 /**
21971  * @class Roo.data.DataProxy
21972  * @extends Roo.data.Observable
21973  * This class is an abstract base class for implementations which provide retrieval of
21974  * unformatted data objects.<br>
21975  * <p>
21976  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21977  * (of the appropriate type which knows how to parse the data object) to provide a block of
21978  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21979  * <p>
21980  * Custom implementations must implement the load method as described in
21981  * {@link Roo.data.HttpProxy#load}.
21982  */
21983 Roo.data.DataProxy = function(){
21984     this.addEvents({
21985         /**
21986          * @event beforeload
21987          * Fires before a network request is made to retrieve a data object.
21988          * @param {Object} This DataProxy object.
21989          * @param {Object} params The params parameter to the load function.
21990          */
21991         beforeload : true,
21992         /**
21993          * @event load
21994          * Fires before the load method's callback is called.
21995          * @param {Object} This DataProxy object.
21996          * @param {Object} o The data object.
21997          * @param {Object} arg The callback argument object passed to the load function.
21998          */
21999         load : true,
22000         /**
22001          * @event loadexception
22002          * Fires if an Exception occurs during data retrieval.
22003          * @param {Object} This DataProxy object.
22004          * @param {Object} o The data object.
22005          * @param {Object} arg The callback argument object passed to the load function.
22006          * @param {Object} e The Exception.
22007          */
22008         loadexception : true
22009     });
22010     Roo.data.DataProxy.superclass.constructor.call(this);
22011 };
22012
22013 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22014
22015     /**
22016      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22017      */
22018 /*
22019  * Based on:
22020  * Ext JS Library 1.1.1
22021  * Copyright(c) 2006-2007, Ext JS, LLC.
22022  *
22023  * Originally Released Under LGPL - original licence link has changed is not relivant.
22024  *
22025  * Fork - LGPL
22026  * <script type="text/javascript">
22027  */
22028 /**
22029  * @class Roo.data.MemoryProxy
22030  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22031  * to the Reader when its load method is called.
22032  * @constructor
22033  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22034  */
22035 Roo.data.MemoryProxy = function(data){
22036     if (data.data) {
22037         data = data.data;
22038     }
22039     Roo.data.MemoryProxy.superclass.constructor.call(this);
22040     this.data = data;
22041 };
22042
22043 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22044     /**
22045      * Load data from the requested source (in this case an in-memory
22046      * data object passed to the constructor), read the data object into
22047      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22048      * process that block using the passed callback.
22049      * @param {Object} params This parameter is not used by the MemoryProxy class.
22050      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22051      * object into a block of Roo.data.Records.
22052      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22053      * The function must be passed <ul>
22054      * <li>The Record block object</li>
22055      * <li>The "arg" argument from the load function</li>
22056      * <li>A boolean success indicator</li>
22057      * </ul>
22058      * @param {Object} scope The scope in which to call the callback
22059      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22060      */
22061     load : function(params, reader, callback, scope, arg){
22062         params = params || {};
22063         var result;
22064         try {
22065             result = reader.readRecords(this.data);
22066         }catch(e){
22067             this.fireEvent("loadexception", this, arg, null, e);
22068             callback.call(scope, null, arg, false);
22069             return;
22070         }
22071         callback.call(scope, result, arg, true);
22072     },
22073     
22074     // private
22075     update : function(params, records){
22076         
22077     }
22078 });/*
22079  * Based on:
22080  * Ext JS Library 1.1.1
22081  * Copyright(c) 2006-2007, Ext JS, LLC.
22082  *
22083  * Originally Released Under LGPL - original licence link has changed is not relivant.
22084  *
22085  * Fork - LGPL
22086  * <script type="text/javascript">
22087  */
22088 /**
22089  * @class Roo.data.HttpProxy
22090  * @extends Roo.data.DataProxy
22091  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22092  * configured to reference a certain URL.<br><br>
22093  * <p>
22094  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22095  * from which the running page was served.<br><br>
22096  * <p>
22097  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22098  * <p>
22099  * Be aware that to enable the browser to parse an XML document, the server must set
22100  * the Content-Type header in the HTTP response to "text/xml".
22101  * @constructor
22102  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22103  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22104  * will be used to make the request.
22105  */
22106 Roo.data.HttpProxy = function(conn){
22107     Roo.data.HttpProxy.superclass.constructor.call(this);
22108     // is conn a conn config or a real conn?
22109     this.conn = conn;
22110     this.useAjax = !conn || !conn.events;
22111   
22112 };
22113
22114 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22115     // thse are take from connection...
22116     
22117     /**
22118      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22119      */
22120     /**
22121      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22122      * extra parameters to each request made by this object. (defaults to undefined)
22123      */
22124     /**
22125      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22126      *  to each request made by this object. (defaults to undefined)
22127      */
22128     /**
22129      * @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)
22130      */
22131     /**
22132      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22133      */
22134      /**
22135      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22136      * @type Boolean
22137      */
22138   
22139
22140     /**
22141      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22142      * @type Boolean
22143      */
22144     /**
22145      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22146      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22147      * a finer-grained basis than the DataProxy events.
22148      */
22149     getConnection : function(){
22150         return this.useAjax ? Roo.Ajax : this.conn;
22151     },
22152
22153     /**
22154      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22155      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22156      * process that block using the passed callback.
22157      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22158      * for the request to the remote server.
22159      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22160      * object into a block of Roo.data.Records.
22161      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22162      * The function must be passed <ul>
22163      * <li>The Record block object</li>
22164      * <li>The "arg" argument from the load function</li>
22165      * <li>A boolean success indicator</li>
22166      * </ul>
22167      * @param {Object} scope The scope in which to call the callback
22168      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22169      */
22170     load : function(params, reader, callback, scope, arg){
22171         if(this.fireEvent("beforeload", this, params) !== false){
22172             var  o = {
22173                 params : params || {},
22174                 request: {
22175                     callback : callback,
22176                     scope : scope,
22177                     arg : arg
22178                 },
22179                 reader: reader,
22180                 callback : this.loadResponse,
22181                 scope: this
22182             };
22183             if(this.useAjax){
22184                 Roo.applyIf(o, this.conn);
22185                 if(this.activeRequest){
22186                     Roo.Ajax.abort(this.activeRequest);
22187                 }
22188                 this.activeRequest = Roo.Ajax.request(o);
22189             }else{
22190                 this.conn.request(o);
22191             }
22192         }else{
22193             callback.call(scope||this, null, arg, false);
22194         }
22195     },
22196
22197     // private
22198     loadResponse : function(o, success, response){
22199         delete this.activeRequest;
22200         if(!success){
22201             this.fireEvent("loadexception", this, o, response);
22202             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22203             return;
22204         }
22205         var result;
22206         try {
22207             result = o.reader.read(response);
22208         }catch(e){
22209             this.fireEvent("loadexception", this, o, response, e);
22210             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22211             return;
22212         }
22213         
22214         this.fireEvent("load", this, o, o.request.arg);
22215         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22216     },
22217
22218     // private
22219     update : function(dataSet){
22220
22221     },
22222
22223     // private
22224     updateResponse : function(dataSet){
22225
22226     }
22227 });/*
22228  * Based on:
22229  * Ext JS Library 1.1.1
22230  * Copyright(c) 2006-2007, Ext JS, LLC.
22231  *
22232  * Originally Released Under LGPL - original licence link has changed is not relivant.
22233  *
22234  * Fork - LGPL
22235  * <script type="text/javascript">
22236  */
22237
22238 /**
22239  * @class Roo.data.ScriptTagProxy
22240  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22241  * other than the originating domain of the running page.<br><br>
22242  * <p>
22243  * <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
22244  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22245  * <p>
22246  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22247  * source code that is used as the source inside a &lt;script> tag.<br><br>
22248  * <p>
22249  * In order for the browser to process the returned data, the server must wrap the data object
22250  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22251  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22252  * depending on whether the callback name was passed:
22253  * <p>
22254  * <pre><code>
22255 boolean scriptTag = false;
22256 String cb = request.getParameter("callback");
22257 if (cb != null) {
22258     scriptTag = true;
22259     response.setContentType("text/javascript");
22260 } else {
22261     response.setContentType("application/x-json");
22262 }
22263 Writer out = response.getWriter();
22264 if (scriptTag) {
22265     out.write(cb + "(");
22266 }
22267 out.print(dataBlock.toJsonString());
22268 if (scriptTag) {
22269     out.write(");");
22270 }
22271 </pre></code>
22272  *
22273  * @constructor
22274  * @param {Object} config A configuration object.
22275  */
22276 Roo.data.ScriptTagProxy = function(config){
22277     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22278     Roo.apply(this, config);
22279     this.head = document.getElementsByTagName("head")[0];
22280 };
22281
22282 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22283
22284 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22285     /**
22286      * @cfg {String} url The URL from which to request the data object.
22287      */
22288     /**
22289      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22290      */
22291     timeout : 30000,
22292     /**
22293      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22294      * the server the name of the callback function set up by the load call to process the returned data object.
22295      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22296      * javascript output which calls this named function passing the data object as its only parameter.
22297      */
22298     callbackParam : "callback",
22299     /**
22300      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22301      * name to the request.
22302      */
22303     nocache : true,
22304
22305     /**
22306      * Load data from the configured URL, read the data object into
22307      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22308      * process that block using the passed callback.
22309      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22310      * for the request to the remote server.
22311      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22312      * object into a block of Roo.data.Records.
22313      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22314      * The function must be passed <ul>
22315      * <li>The Record block object</li>
22316      * <li>The "arg" argument from the load function</li>
22317      * <li>A boolean success indicator</li>
22318      * </ul>
22319      * @param {Object} scope The scope in which to call the callback
22320      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22321      */
22322     load : function(params, reader, callback, scope, arg){
22323         if(this.fireEvent("beforeload", this, params) !== false){
22324
22325             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22326
22327             var url = this.url;
22328             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22329             if(this.nocache){
22330                 url += "&_dc=" + (new Date().getTime());
22331             }
22332             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22333             var trans = {
22334                 id : transId,
22335                 cb : "stcCallback"+transId,
22336                 scriptId : "stcScript"+transId,
22337                 params : params,
22338                 arg : arg,
22339                 url : url,
22340                 callback : callback,
22341                 scope : scope,
22342                 reader : reader
22343             };
22344             var conn = this;
22345
22346             window[trans.cb] = function(o){
22347                 conn.handleResponse(o, trans);
22348             };
22349
22350             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22351
22352             if(this.autoAbort !== false){
22353                 this.abort();
22354             }
22355
22356             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22357
22358             var script = document.createElement("script");
22359             script.setAttribute("src", url);
22360             script.setAttribute("type", "text/javascript");
22361             script.setAttribute("id", trans.scriptId);
22362             this.head.appendChild(script);
22363
22364             this.trans = trans;
22365         }else{
22366             callback.call(scope||this, null, arg, false);
22367         }
22368     },
22369
22370     // private
22371     isLoading : function(){
22372         return this.trans ? true : false;
22373     },
22374
22375     /**
22376      * Abort the current server request.
22377      */
22378     abort : function(){
22379         if(this.isLoading()){
22380             this.destroyTrans(this.trans);
22381         }
22382     },
22383
22384     // private
22385     destroyTrans : function(trans, isLoaded){
22386         this.head.removeChild(document.getElementById(trans.scriptId));
22387         clearTimeout(trans.timeoutId);
22388         if(isLoaded){
22389             window[trans.cb] = undefined;
22390             try{
22391                 delete window[trans.cb];
22392             }catch(e){}
22393         }else{
22394             // if hasn't been loaded, wait for load to remove it to prevent script error
22395             window[trans.cb] = function(){
22396                 window[trans.cb] = undefined;
22397                 try{
22398                     delete window[trans.cb];
22399                 }catch(e){}
22400             };
22401         }
22402     },
22403
22404     // private
22405     handleResponse : function(o, trans){
22406         this.trans = false;
22407         this.destroyTrans(trans, true);
22408         var result;
22409         try {
22410             result = trans.reader.readRecords(o);
22411         }catch(e){
22412             this.fireEvent("loadexception", this, o, trans.arg, e);
22413             trans.callback.call(trans.scope||window, null, trans.arg, false);
22414             return;
22415         }
22416         this.fireEvent("load", this, o, trans.arg);
22417         trans.callback.call(trans.scope||window, result, trans.arg, true);
22418     },
22419
22420     // private
22421     handleFailure : function(trans){
22422         this.trans = false;
22423         this.destroyTrans(trans, false);
22424         this.fireEvent("loadexception", this, null, trans.arg);
22425         trans.callback.call(trans.scope||window, null, trans.arg, false);
22426     }
22427 });/*
22428  * Based on:
22429  * Ext JS Library 1.1.1
22430  * Copyright(c) 2006-2007, Ext JS, LLC.
22431  *
22432  * Originally Released Under LGPL - original licence link has changed is not relivant.
22433  *
22434  * Fork - LGPL
22435  * <script type="text/javascript">
22436  */
22437
22438 /**
22439  * @class Roo.data.JsonReader
22440  * @extends Roo.data.DataReader
22441  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22442  * based on mappings in a provided Roo.data.Record constructor.
22443  * 
22444  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22445  * in the reply previously. 
22446  * 
22447  * <p>
22448  * Example code:
22449  * <pre><code>
22450 var RecordDef = Roo.data.Record.create([
22451     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22452     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22453 ]);
22454 var myReader = new Roo.data.JsonReader({
22455     totalProperty: "results",    // The property which contains the total dataset size (optional)
22456     root: "rows",                // The property which contains an Array of row objects
22457     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22458 }, RecordDef);
22459 </code></pre>
22460  * <p>
22461  * This would consume a JSON file like this:
22462  * <pre><code>
22463 { 'results': 2, 'rows': [
22464     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22465     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22466 }
22467 </code></pre>
22468  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22469  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22470  * paged from the remote server.
22471  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22472  * @cfg {String} root name of the property which contains the Array of row objects.
22473  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22474  * @constructor
22475  * Create a new JsonReader
22476  * @param {Object} meta Metadata configuration options
22477  * @param {Object} recordType Either an Array of field definition objects,
22478  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22479  */
22480 Roo.data.JsonReader = function(meta, recordType){
22481     
22482     meta = meta || {};
22483     // set some defaults:
22484     Roo.applyIf(meta, {
22485         totalProperty: 'total',
22486         successProperty : 'success',
22487         root : 'data',
22488         id : 'id'
22489     });
22490     
22491     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22492 };
22493 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22494     
22495     /**
22496      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22497      * Used by Store query builder to append _requestMeta to params.
22498      * 
22499      */
22500     metaFromRemote : false,
22501     /**
22502      * This method is only used by a DataProxy which has retrieved data from a remote server.
22503      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22504      * @return {Object} data A data block which is used by an Roo.data.Store object as
22505      * a cache of Roo.data.Records.
22506      */
22507     read : function(response){
22508         var json = response.responseText;
22509        
22510         var o = /* eval:var:o */ eval("("+json+")");
22511         if(!o) {
22512             throw {message: "JsonReader.read: Json object not found"};
22513         }
22514         
22515         if(o.metaData){
22516             
22517             delete this.ef;
22518             this.metaFromRemote = true;
22519             this.meta = o.metaData;
22520             this.recordType = Roo.data.Record.create(o.metaData.fields);
22521             this.onMetaChange(this.meta, this.recordType, o);
22522         }
22523         return this.readRecords(o);
22524     },
22525
22526     // private function a store will implement
22527     onMetaChange : function(meta, recordType, o){
22528
22529     },
22530
22531     /**
22532          * @ignore
22533          */
22534     simpleAccess: function(obj, subsc) {
22535         return obj[subsc];
22536     },
22537
22538         /**
22539          * @ignore
22540          */
22541     getJsonAccessor: function(){
22542         var re = /[\[\.]/;
22543         return function(expr) {
22544             try {
22545                 return(re.test(expr))
22546                     ? new Function("obj", "return obj." + expr)
22547                     : function(obj){
22548                         return obj[expr];
22549                     };
22550             } catch(e){}
22551             return Roo.emptyFn;
22552         };
22553     }(),
22554
22555     /**
22556      * Create a data block containing Roo.data.Records from an XML document.
22557      * @param {Object} o An object which contains an Array of row objects in the property specified
22558      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22559      * which contains the total size of the dataset.
22560      * @return {Object} data A data block which is used by an Roo.data.Store object as
22561      * a cache of Roo.data.Records.
22562      */
22563     readRecords : function(o){
22564         /**
22565          * After any data loads, the raw JSON data is available for further custom processing.
22566          * @type Object
22567          */
22568         this.o = o;
22569         var s = this.meta, Record = this.recordType,
22570             f = Record.prototype.fields, fi = f.items, fl = f.length;
22571
22572 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22573         if (!this.ef) {
22574             if(s.totalProperty) {
22575                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22576                 }
22577                 if(s.successProperty) {
22578                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22579                 }
22580                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22581                 if (s.id) {
22582                         var g = this.getJsonAccessor(s.id);
22583                         this.getId = function(rec) {
22584                                 var r = g(rec);
22585                                 return (r === undefined || r === "") ? null : r;
22586                         };
22587                 } else {
22588                         this.getId = function(){return null;};
22589                 }
22590             this.ef = [];
22591             for(var jj = 0; jj < fl; jj++){
22592                 f = fi[jj];
22593                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22594                 this.ef[jj] = this.getJsonAccessor(map);
22595             }
22596         }
22597
22598         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22599         if(s.totalProperty){
22600             var vt = parseInt(this.getTotal(o), 10);
22601             if(!isNaN(vt)){
22602                 totalRecords = vt;
22603             }
22604         }
22605         if(s.successProperty){
22606             var vs = this.getSuccess(o);
22607             if(vs === false || vs === 'false'){
22608                 success = false;
22609             }
22610         }
22611         var records = [];
22612             for(var i = 0; i < c; i++){
22613                     var n = root[i];
22614                 var values = {};
22615                 var id = this.getId(n);
22616                 for(var j = 0; j < fl; j++){
22617                     f = fi[j];
22618                 var v = this.ef[j](n);
22619                 if (!f.convert) {
22620                     Roo.log('missing convert for ' + f.name);
22621                     Roo.log(f);
22622                     continue;
22623                 }
22624                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22625                 }
22626                 var record = new Record(values, id);
22627                 record.json = n;
22628                 records[i] = record;
22629             }
22630             return {
22631             raw : o,
22632                 success : success,
22633                 records : records,
22634                 totalRecords : totalRecords
22635             };
22636     }
22637 });/*
22638  * Based on:
22639  * Ext JS Library 1.1.1
22640  * Copyright(c) 2006-2007, Ext JS, LLC.
22641  *
22642  * Originally Released Under LGPL - original licence link has changed is not relivant.
22643  *
22644  * Fork - LGPL
22645  * <script type="text/javascript">
22646  */
22647
22648 /**
22649  * @class Roo.data.XmlReader
22650  * @extends Roo.data.DataReader
22651  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22652  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22653  * <p>
22654  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22655  * header in the HTTP response must be set to "text/xml".</em>
22656  * <p>
22657  * Example code:
22658  * <pre><code>
22659 var RecordDef = Roo.data.Record.create([
22660    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22661    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22662 ]);
22663 var myReader = new Roo.data.XmlReader({
22664    totalRecords: "results", // The element which contains the total dataset size (optional)
22665    record: "row",           // The repeated element which contains row information
22666    id: "id"                 // The element within the row that provides an ID for the record (optional)
22667 }, RecordDef);
22668 </code></pre>
22669  * <p>
22670  * This would consume an XML file like this:
22671  * <pre><code>
22672 &lt;?xml?>
22673 &lt;dataset>
22674  &lt;results>2&lt;/results>
22675  &lt;row>
22676    &lt;id>1&lt;/id>
22677    &lt;name>Bill&lt;/name>
22678    &lt;occupation>Gardener&lt;/occupation>
22679  &lt;/row>
22680  &lt;row>
22681    &lt;id>2&lt;/id>
22682    &lt;name>Ben&lt;/name>
22683    &lt;occupation>Horticulturalist&lt;/occupation>
22684  &lt;/row>
22685 &lt;/dataset>
22686 </code></pre>
22687  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22688  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22689  * paged from the remote server.
22690  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22691  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22692  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22693  * a record identifier value.
22694  * @constructor
22695  * Create a new XmlReader
22696  * @param {Object} meta Metadata configuration options
22697  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22698  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22699  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22700  */
22701 Roo.data.XmlReader = function(meta, recordType){
22702     meta = meta || {};
22703     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22704 };
22705 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22706     /**
22707      * This method is only used by a DataProxy which has retrieved data from a remote server.
22708          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22709          * to contain a method called 'responseXML' that returns an XML document object.
22710      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22711      * a cache of Roo.data.Records.
22712      */
22713     read : function(response){
22714         var doc = response.responseXML;
22715         if(!doc) {
22716             throw {message: "XmlReader.read: XML Document not available"};
22717         }
22718         return this.readRecords(doc);
22719     },
22720
22721     /**
22722      * Create a data block containing Roo.data.Records from an XML document.
22723          * @param {Object} doc A parsed XML document.
22724      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22725      * a cache of Roo.data.Records.
22726      */
22727     readRecords : function(doc){
22728         /**
22729          * After any data loads/reads, the raw XML Document is available for further custom processing.
22730          * @type XMLDocument
22731          */
22732         this.xmlData = doc;
22733         var root = doc.documentElement || doc;
22734         var q = Roo.DomQuery;
22735         var recordType = this.recordType, fields = recordType.prototype.fields;
22736         var sid = this.meta.id;
22737         var totalRecords = 0, success = true;
22738         if(this.meta.totalRecords){
22739             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22740         }
22741         
22742         if(this.meta.success){
22743             var sv = q.selectValue(this.meta.success, root, true);
22744             success = sv !== false && sv !== 'false';
22745         }
22746         var records = [];
22747         var ns = q.select(this.meta.record, root);
22748         for(var i = 0, len = ns.length; i < len; i++) {
22749                 var n = ns[i];
22750                 var values = {};
22751                 var id = sid ? q.selectValue(sid, n) : undefined;
22752                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22753                     var f = fields.items[j];
22754                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22755                     v = f.convert(v);
22756                     values[f.name] = v;
22757                 }
22758                 var record = new recordType(values, id);
22759                 record.node = n;
22760                 records[records.length] = record;
22761             }
22762
22763             return {
22764                 success : success,
22765                 records : records,
22766                 totalRecords : totalRecords || records.length
22767             };
22768     }
22769 });/*
22770  * Based on:
22771  * Ext JS Library 1.1.1
22772  * Copyright(c) 2006-2007, Ext JS, LLC.
22773  *
22774  * Originally Released Under LGPL - original licence link has changed is not relivant.
22775  *
22776  * Fork - LGPL
22777  * <script type="text/javascript">
22778  */
22779
22780 /**
22781  * @class Roo.data.ArrayReader
22782  * @extends Roo.data.DataReader
22783  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22784  * Each element of that Array represents a row of data fields. The
22785  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22786  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22787  * <p>
22788  * Example code:.
22789  * <pre><code>
22790 var RecordDef = Roo.data.Record.create([
22791     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22792     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22793 ]);
22794 var myReader = new Roo.data.ArrayReader({
22795     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22796 }, RecordDef);
22797 </code></pre>
22798  * <p>
22799  * This would consume an Array like this:
22800  * <pre><code>
22801 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22802   </code></pre>
22803  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22804  * @constructor
22805  * Create a new JsonReader
22806  * @param {Object} meta Metadata configuration options.
22807  * @param {Object} recordType Either an Array of field definition objects
22808  * as specified to {@link Roo.data.Record#create},
22809  * or an {@link Roo.data.Record} object
22810  * created using {@link Roo.data.Record#create}.
22811  */
22812 Roo.data.ArrayReader = function(meta, recordType){
22813     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22814 };
22815
22816 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22817     /**
22818      * Create a data block containing Roo.data.Records from an XML document.
22819      * @param {Object} o An Array of row objects which represents the dataset.
22820      * @return {Object} data A data block which is used by an Roo.data.Store object as
22821      * a cache of Roo.data.Records.
22822      */
22823     readRecords : function(o){
22824         var sid = this.meta ? this.meta.id : null;
22825         var recordType = this.recordType, fields = recordType.prototype.fields;
22826         var records = [];
22827         var root = o;
22828             for(var i = 0; i < root.length; i++){
22829                     var n = root[i];
22830                 var values = {};
22831                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22832                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22833                 var f = fields.items[j];
22834                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22835                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22836                 v = f.convert(v);
22837                 values[f.name] = v;
22838             }
22839                 var record = new recordType(values, id);
22840                 record.json = n;
22841                 records[records.length] = record;
22842             }
22843             return {
22844                 records : records,
22845                 totalRecords : records.length
22846             };
22847     }
22848 });/*
22849  * Based on:
22850  * Ext JS Library 1.1.1
22851  * Copyright(c) 2006-2007, Ext JS, LLC.
22852  *
22853  * Originally Released Under LGPL - original licence link has changed is not relivant.
22854  *
22855  * Fork - LGPL
22856  * <script type="text/javascript">
22857  */
22858
22859
22860 /**
22861  * @class Roo.data.Tree
22862  * @extends Roo.util.Observable
22863  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22864  * in the tree have most standard DOM functionality.
22865  * @constructor
22866  * @param {Node} root (optional) The root node
22867  */
22868 Roo.data.Tree = function(root){
22869    this.nodeHash = {};
22870    /**
22871     * The root node for this tree
22872     * @type Node
22873     */
22874    this.root = null;
22875    if(root){
22876        this.setRootNode(root);
22877    }
22878    this.addEvents({
22879        /**
22880         * @event append
22881         * Fires when a new child node is appended to a node in this tree.
22882         * @param {Tree} tree The owner tree
22883         * @param {Node} parent The parent node
22884         * @param {Node} node The newly appended node
22885         * @param {Number} index The index of the newly appended node
22886         */
22887        "append" : true,
22888        /**
22889         * @event remove
22890         * Fires when a child node is removed from a node in this tree.
22891         * @param {Tree} tree The owner tree
22892         * @param {Node} parent The parent node
22893         * @param {Node} node The child node removed
22894         */
22895        "remove" : true,
22896        /**
22897         * @event move
22898         * Fires when a node is moved to a new location in the tree
22899         * @param {Tree} tree The owner tree
22900         * @param {Node} node The node moved
22901         * @param {Node} oldParent The old parent of this node
22902         * @param {Node} newParent The new parent of this node
22903         * @param {Number} index The index it was moved to
22904         */
22905        "move" : true,
22906        /**
22907         * @event insert
22908         * Fires when a new child node is inserted in a node in this tree.
22909         * @param {Tree} tree The owner tree
22910         * @param {Node} parent The parent node
22911         * @param {Node} node The child node inserted
22912         * @param {Node} refNode The child node the node was inserted before
22913         */
22914        "insert" : true,
22915        /**
22916         * @event beforeappend
22917         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22918         * @param {Tree} tree The owner tree
22919         * @param {Node} parent The parent node
22920         * @param {Node} node The child node to be appended
22921         */
22922        "beforeappend" : true,
22923        /**
22924         * @event beforeremove
22925         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22926         * @param {Tree} tree The owner tree
22927         * @param {Node} parent The parent node
22928         * @param {Node} node The child node to be removed
22929         */
22930        "beforeremove" : true,
22931        /**
22932         * @event beforemove
22933         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22934         * @param {Tree} tree The owner tree
22935         * @param {Node} node The node being moved
22936         * @param {Node} oldParent The parent of the node
22937         * @param {Node} newParent The new parent the node is moving to
22938         * @param {Number} index The index it is being moved to
22939         */
22940        "beforemove" : true,
22941        /**
22942         * @event beforeinsert
22943         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22944         * @param {Tree} tree The owner tree
22945         * @param {Node} parent The parent node
22946         * @param {Node} node The child node to be inserted
22947         * @param {Node} refNode The child node the node is being inserted before
22948         */
22949        "beforeinsert" : true
22950    });
22951
22952     Roo.data.Tree.superclass.constructor.call(this);
22953 };
22954
22955 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22956     pathSeparator: "/",
22957
22958     proxyNodeEvent : function(){
22959         return this.fireEvent.apply(this, arguments);
22960     },
22961
22962     /**
22963      * Returns the root node for this tree.
22964      * @return {Node}
22965      */
22966     getRootNode : function(){
22967         return this.root;
22968     },
22969
22970     /**
22971      * Sets the root node for this tree.
22972      * @param {Node} node
22973      * @return {Node}
22974      */
22975     setRootNode : function(node){
22976         this.root = node;
22977         node.ownerTree = this;
22978         node.isRoot = true;
22979         this.registerNode(node);
22980         return node;
22981     },
22982
22983     /**
22984      * Gets a node in this tree by its id.
22985      * @param {String} id
22986      * @return {Node}
22987      */
22988     getNodeById : function(id){
22989         return this.nodeHash[id];
22990     },
22991
22992     registerNode : function(node){
22993         this.nodeHash[node.id] = node;
22994     },
22995
22996     unregisterNode : function(node){
22997         delete this.nodeHash[node.id];
22998     },
22999
23000     toString : function(){
23001         return "[Tree"+(this.id?" "+this.id:"")+"]";
23002     }
23003 });
23004
23005 /**
23006  * @class Roo.data.Node
23007  * @extends Roo.util.Observable
23008  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23009  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23010  * @constructor
23011  * @param {Object} attributes The attributes/config for the node
23012  */
23013 Roo.data.Node = function(attributes){
23014     /**
23015      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23016      * @type {Object}
23017      */
23018     this.attributes = attributes || {};
23019     this.leaf = this.attributes.leaf;
23020     /**
23021      * The node id. @type String
23022      */
23023     this.id = this.attributes.id;
23024     if(!this.id){
23025         this.id = Roo.id(null, "ynode-");
23026         this.attributes.id = this.id;
23027     }
23028      
23029     
23030     /**
23031      * All child nodes of this node. @type Array
23032      */
23033     this.childNodes = [];
23034     if(!this.childNodes.indexOf){ // indexOf is a must
23035         this.childNodes.indexOf = function(o){
23036             for(var i = 0, len = this.length; i < len; i++){
23037                 if(this[i] == o) {
23038                     return i;
23039                 }
23040             }
23041             return -1;
23042         };
23043     }
23044     /**
23045      * The parent node for this node. @type Node
23046      */
23047     this.parentNode = null;
23048     /**
23049      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23050      */
23051     this.firstChild = null;
23052     /**
23053      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23054      */
23055     this.lastChild = null;
23056     /**
23057      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23058      */
23059     this.previousSibling = null;
23060     /**
23061      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23062      */
23063     this.nextSibling = null;
23064
23065     this.addEvents({
23066        /**
23067         * @event append
23068         * Fires when a new child node is appended
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} this This node
23071         * @param {Node} node The newly appended node
23072         * @param {Number} index The index of the newly appended node
23073         */
23074        "append" : true,
23075        /**
23076         * @event remove
23077         * Fires when a child node is removed
23078         * @param {Tree} tree The owner tree
23079         * @param {Node} this This node
23080         * @param {Node} node The removed node
23081         */
23082        "remove" : true,
23083        /**
23084         * @event move
23085         * Fires when this node is moved to a new location in the tree
23086         * @param {Tree} tree The owner tree
23087         * @param {Node} this This node
23088         * @param {Node} oldParent The old parent of this node
23089         * @param {Node} newParent The new parent of this node
23090         * @param {Number} index The index it was moved to
23091         */
23092        "move" : true,
23093        /**
23094         * @event insert
23095         * Fires when a new child node is inserted.
23096         * @param {Tree} tree The owner tree
23097         * @param {Node} this This node
23098         * @param {Node} node The child node inserted
23099         * @param {Node} refNode The child node the node was inserted before
23100         */
23101        "insert" : true,
23102        /**
23103         * @event beforeappend
23104         * Fires before a new child is appended, return false to cancel the append.
23105         * @param {Tree} tree The owner tree
23106         * @param {Node} this This node
23107         * @param {Node} node The child node to be appended
23108         */
23109        "beforeappend" : true,
23110        /**
23111         * @event beforeremove
23112         * Fires before a child is removed, return false to cancel the remove.
23113         * @param {Tree} tree The owner tree
23114         * @param {Node} this This node
23115         * @param {Node} node The child node to be removed
23116         */
23117        "beforeremove" : true,
23118        /**
23119         * @event beforemove
23120         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23121         * @param {Tree} tree The owner tree
23122         * @param {Node} this This node
23123         * @param {Node} oldParent The parent of this node
23124         * @param {Node} newParent The new parent this node is moving to
23125         * @param {Number} index The index it is being moved to
23126         */
23127        "beforemove" : true,
23128        /**
23129         * @event beforeinsert
23130         * Fires before a new child is inserted, return false to cancel the insert.
23131         * @param {Tree} tree The owner tree
23132         * @param {Node} this This node
23133         * @param {Node} node The child node to be inserted
23134         * @param {Node} refNode The child node the node is being inserted before
23135         */
23136        "beforeinsert" : true
23137    });
23138     this.listeners = this.attributes.listeners;
23139     Roo.data.Node.superclass.constructor.call(this);
23140 };
23141
23142 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23143     fireEvent : function(evtName){
23144         // first do standard event for this node
23145         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23146             return false;
23147         }
23148         // then bubble it up to the tree if the event wasn't cancelled
23149         var ot = this.getOwnerTree();
23150         if(ot){
23151             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23152                 return false;
23153             }
23154         }
23155         return true;
23156     },
23157
23158     /**
23159      * Returns true if this node is a leaf
23160      * @return {Boolean}
23161      */
23162     isLeaf : function(){
23163         return this.leaf === true;
23164     },
23165
23166     // private
23167     setFirstChild : function(node){
23168         this.firstChild = node;
23169     },
23170
23171     //private
23172     setLastChild : function(node){
23173         this.lastChild = node;
23174     },
23175
23176
23177     /**
23178      * Returns true if this node is the last child of its parent
23179      * @return {Boolean}
23180      */
23181     isLast : function(){
23182        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23183     },
23184
23185     /**
23186      * Returns true if this node is the first child of its parent
23187      * @return {Boolean}
23188      */
23189     isFirst : function(){
23190        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23191     },
23192
23193     hasChildNodes : function(){
23194         return !this.isLeaf() && this.childNodes.length > 0;
23195     },
23196
23197     /**
23198      * Insert node(s) as the last child node of this node.
23199      * @param {Node/Array} node The node or Array of nodes to append
23200      * @return {Node} The appended node if single append, or null if an array was passed
23201      */
23202     appendChild : function(node){
23203         var multi = false;
23204         if(node instanceof Array){
23205             multi = node;
23206         }else if(arguments.length > 1){
23207             multi = arguments;
23208         }
23209         // if passed an array or multiple args do them one by one
23210         if(multi){
23211             for(var i = 0, len = multi.length; i < len; i++) {
23212                 this.appendChild(multi[i]);
23213             }
23214         }else{
23215             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23216                 return false;
23217             }
23218             var index = this.childNodes.length;
23219             var oldParent = node.parentNode;
23220             // it's a move, make sure we move it cleanly
23221             if(oldParent){
23222                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23223                     return false;
23224                 }
23225                 oldParent.removeChild(node);
23226             }
23227             index = this.childNodes.length;
23228             if(index == 0){
23229                 this.setFirstChild(node);
23230             }
23231             this.childNodes.push(node);
23232             node.parentNode = this;
23233             var ps = this.childNodes[index-1];
23234             if(ps){
23235                 node.previousSibling = ps;
23236                 ps.nextSibling = node;
23237             }else{
23238                 node.previousSibling = null;
23239             }
23240             node.nextSibling = null;
23241             this.setLastChild(node);
23242             node.setOwnerTree(this.getOwnerTree());
23243             this.fireEvent("append", this.ownerTree, this, node, index);
23244             if(oldParent){
23245                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23246             }
23247             return node;
23248         }
23249     },
23250
23251     /**
23252      * Removes a child node from this node.
23253      * @param {Node} node The node to remove
23254      * @return {Node} The removed node
23255      */
23256     removeChild : function(node){
23257         var index = this.childNodes.indexOf(node);
23258         if(index == -1){
23259             return false;
23260         }
23261         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23262             return false;
23263         }
23264
23265         // remove it from childNodes collection
23266         this.childNodes.splice(index, 1);
23267
23268         // update siblings
23269         if(node.previousSibling){
23270             node.previousSibling.nextSibling = node.nextSibling;
23271         }
23272         if(node.nextSibling){
23273             node.nextSibling.previousSibling = node.previousSibling;
23274         }
23275
23276         // update child refs
23277         if(this.firstChild == node){
23278             this.setFirstChild(node.nextSibling);
23279         }
23280         if(this.lastChild == node){
23281             this.setLastChild(node.previousSibling);
23282         }
23283
23284         node.setOwnerTree(null);
23285         // clear any references from the node
23286         node.parentNode = null;
23287         node.previousSibling = null;
23288         node.nextSibling = null;
23289         this.fireEvent("remove", this.ownerTree, this, node);
23290         return node;
23291     },
23292
23293     /**
23294      * Inserts the first node before the second node in this nodes childNodes collection.
23295      * @param {Node} node The node to insert
23296      * @param {Node} refNode The node to insert before (if null the node is appended)
23297      * @return {Node} The inserted node
23298      */
23299     insertBefore : function(node, refNode){
23300         if(!refNode){ // like standard Dom, refNode can be null for append
23301             return this.appendChild(node);
23302         }
23303         // nothing to do
23304         if(node == refNode){
23305             return false;
23306         }
23307
23308         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23309             return false;
23310         }
23311         var index = this.childNodes.indexOf(refNode);
23312         var oldParent = node.parentNode;
23313         var refIndex = index;
23314
23315         // when moving internally, indexes will change after remove
23316         if(oldParent == this && this.childNodes.indexOf(node) < index){
23317             refIndex--;
23318         }
23319
23320         // it's a move, make sure we move it cleanly
23321         if(oldParent){
23322             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23323                 return false;
23324             }
23325             oldParent.removeChild(node);
23326         }
23327         if(refIndex == 0){
23328             this.setFirstChild(node);
23329         }
23330         this.childNodes.splice(refIndex, 0, node);
23331         node.parentNode = this;
23332         var ps = this.childNodes[refIndex-1];
23333         if(ps){
23334             node.previousSibling = ps;
23335             ps.nextSibling = node;
23336         }else{
23337             node.previousSibling = null;
23338         }
23339         node.nextSibling = refNode;
23340         refNode.previousSibling = node;
23341         node.setOwnerTree(this.getOwnerTree());
23342         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23343         if(oldParent){
23344             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23345         }
23346         return node;
23347     },
23348
23349     /**
23350      * Returns the child node at the specified index.
23351      * @param {Number} index
23352      * @return {Node}
23353      */
23354     item : function(index){
23355         return this.childNodes[index];
23356     },
23357
23358     /**
23359      * Replaces one child node in this node with another.
23360      * @param {Node} newChild The replacement node
23361      * @param {Node} oldChild The node to replace
23362      * @return {Node} The replaced node
23363      */
23364     replaceChild : function(newChild, oldChild){
23365         this.insertBefore(newChild, oldChild);
23366         this.removeChild(oldChild);
23367         return oldChild;
23368     },
23369
23370     /**
23371      * Returns the index of a child node
23372      * @param {Node} node
23373      * @return {Number} The index of the node or -1 if it was not found
23374      */
23375     indexOf : function(child){
23376         return this.childNodes.indexOf(child);
23377     },
23378
23379     /**
23380      * Returns the tree this node is in.
23381      * @return {Tree}
23382      */
23383     getOwnerTree : function(){
23384         // if it doesn't have one, look for one
23385         if(!this.ownerTree){
23386             var p = this;
23387             while(p){
23388                 if(p.ownerTree){
23389                     this.ownerTree = p.ownerTree;
23390                     break;
23391                 }
23392                 p = p.parentNode;
23393             }
23394         }
23395         return this.ownerTree;
23396     },
23397
23398     /**
23399      * Returns depth of this node (the root node has a depth of 0)
23400      * @return {Number}
23401      */
23402     getDepth : function(){
23403         var depth = 0;
23404         var p = this;
23405         while(p.parentNode){
23406             ++depth;
23407             p = p.parentNode;
23408         }
23409         return depth;
23410     },
23411
23412     // private
23413     setOwnerTree : function(tree){
23414         // if it's move, we need to update everyone
23415         if(tree != this.ownerTree){
23416             if(this.ownerTree){
23417                 this.ownerTree.unregisterNode(this);
23418             }
23419             this.ownerTree = tree;
23420             var cs = this.childNodes;
23421             for(var i = 0, len = cs.length; i < len; i++) {
23422                 cs[i].setOwnerTree(tree);
23423             }
23424             if(tree){
23425                 tree.registerNode(this);
23426             }
23427         }
23428     },
23429
23430     /**
23431      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23432      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23433      * @return {String} The path
23434      */
23435     getPath : function(attr){
23436         attr = attr || "id";
23437         var p = this.parentNode;
23438         var b = [this.attributes[attr]];
23439         while(p){
23440             b.unshift(p.attributes[attr]);
23441             p = p.parentNode;
23442         }
23443         var sep = this.getOwnerTree().pathSeparator;
23444         return sep + b.join(sep);
23445     },
23446
23447     /**
23448      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23449      * function call will be the scope provided or the current node. The arguments to the function
23450      * will be the args provided or the current node. If the function returns false at any point,
23451      * the bubble is stopped.
23452      * @param {Function} fn The function to call
23453      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23454      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23455      */
23456     bubble : function(fn, scope, args){
23457         var p = this;
23458         while(p){
23459             if(fn.call(scope || p, args || p) === false){
23460                 break;
23461             }
23462             p = p.parentNode;
23463         }
23464     },
23465
23466     /**
23467      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23468      * function call will be the scope provided or the current node. The arguments to the function
23469      * will be the args provided or the current node. If the function returns false at any point,
23470      * the cascade is stopped on that branch.
23471      * @param {Function} fn The function to call
23472      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23473      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23474      */
23475     cascade : function(fn, scope, args){
23476         if(fn.call(scope || this, args || this) !== false){
23477             var cs = this.childNodes;
23478             for(var i = 0, len = cs.length; i < len; i++) {
23479                 cs[i].cascade(fn, scope, args);
23480             }
23481         }
23482     },
23483
23484     /**
23485      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23486      * function call will be the scope provided or the current node. The arguments to the function
23487      * will be the args provided or the current node. If the function returns false at any point,
23488      * the iteration stops.
23489      * @param {Function} fn The function to call
23490      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23491      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23492      */
23493     eachChild : function(fn, scope, args){
23494         var cs = this.childNodes;
23495         for(var i = 0, len = cs.length; i < len; i++) {
23496                 if(fn.call(scope || this, args || cs[i]) === false){
23497                     break;
23498                 }
23499         }
23500     },
23501
23502     /**
23503      * Finds the first child that has the attribute with the specified value.
23504      * @param {String} attribute The attribute name
23505      * @param {Mixed} value The value to search for
23506      * @return {Node} The found child or null if none was found
23507      */
23508     findChild : function(attribute, value){
23509         var cs = this.childNodes;
23510         for(var i = 0, len = cs.length; i < len; i++) {
23511                 if(cs[i].attributes[attribute] == value){
23512                     return cs[i];
23513                 }
23514         }
23515         return null;
23516     },
23517
23518     /**
23519      * Finds the first child by a custom function. The child matches if the function passed
23520      * returns true.
23521      * @param {Function} fn
23522      * @param {Object} scope (optional)
23523      * @return {Node} The found child or null if none was found
23524      */
23525     findChildBy : function(fn, scope){
23526         var cs = this.childNodes;
23527         for(var i = 0, len = cs.length; i < len; i++) {
23528                 if(fn.call(scope||cs[i], cs[i]) === true){
23529                     return cs[i];
23530                 }
23531         }
23532         return null;
23533     },
23534
23535     /**
23536      * Sorts this nodes children using the supplied sort function
23537      * @param {Function} fn
23538      * @param {Object} scope (optional)
23539      */
23540     sort : function(fn, scope){
23541         var cs = this.childNodes;
23542         var len = cs.length;
23543         if(len > 0){
23544             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23545             cs.sort(sortFn);
23546             for(var i = 0; i < len; i++){
23547                 var n = cs[i];
23548                 n.previousSibling = cs[i-1];
23549                 n.nextSibling = cs[i+1];
23550                 if(i == 0){
23551                     this.setFirstChild(n);
23552                 }
23553                 if(i == len-1){
23554                     this.setLastChild(n);
23555                 }
23556             }
23557         }
23558     },
23559
23560     /**
23561      * Returns true if this node is an ancestor (at any point) of the passed node.
23562      * @param {Node} node
23563      * @return {Boolean}
23564      */
23565     contains : function(node){
23566         return node.isAncestor(this);
23567     },
23568
23569     /**
23570      * Returns true if the passed node is an ancestor (at any point) of this node.
23571      * @param {Node} node
23572      * @return {Boolean}
23573      */
23574     isAncestor : function(node){
23575         var p = this.parentNode;
23576         while(p){
23577             if(p == node){
23578                 return true;
23579             }
23580             p = p.parentNode;
23581         }
23582         return false;
23583     },
23584
23585     toString : function(){
23586         return "[Node"+(this.id?" "+this.id:"")+"]";
23587     }
23588 });/*
23589  * Based on:
23590  * Ext JS Library 1.1.1
23591  * Copyright(c) 2006-2007, Ext JS, LLC.
23592  *
23593  * Originally Released Under LGPL - original licence link has changed is not relivant.
23594  *
23595  * Fork - LGPL
23596  * <script type="text/javascript">
23597  */
23598  (function(){ 
23599 /**
23600  * @class Roo.Layer
23601  * @extends Roo.Element
23602  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23603  * automatic maintaining of shadow/shim positions.
23604  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23605  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23606  * you can pass a string with a CSS class name. False turns off the shadow.
23607  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23608  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23609  * @cfg {String} cls CSS class to add to the element
23610  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23611  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23612  * @constructor
23613  * @param {Object} config An object with config options.
23614  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23615  */
23616
23617 Roo.Layer = function(config, existingEl){
23618     config = config || {};
23619     var dh = Roo.DomHelper;
23620     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23621     if(existingEl){
23622         this.dom = Roo.getDom(existingEl);
23623     }
23624     if(!this.dom){
23625         var o = config.dh || {tag: "div", cls: "x-layer"};
23626         this.dom = dh.append(pel, o);
23627     }
23628     if(config.cls){
23629         this.addClass(config.cls);
23630     }
23631     this.constrain = config.constrain !== false;
23632     this.visibilityMode = Roo.Element.VISIBILITY;
23633     if(config.id){
23634         this.id = this.dom.id = config.id;
23635     }else{
23636         this.id = Roo.id(this.dom);
23637     }
23638     this.zindex = config.zindex || this.getZIndex();
23639     this.position("absolute", this.zindex);
23640     if(config.shadow){
23641         this.shadowOffset = config.shadowOffset || 4;
23642         this.shadow = new Roo.Shadow({
23643             offset : this.shadowOffset,
23644             mode : config.shadow
23645         });
23646     }else{
23647         this.shadowOffset = 0;
23648     }
23649     this.useShim = config.shim !== false && Roo.useShims;
23650     this.useDisplay = config.useDisplay;
23651     this.hide();
23652 };
23653
23654 var supr = Roo.Element.prototype;
23655
23656 // shims are shared among layer to keep from having 100 iframes
23657 var shims = [];
23658
23659 Roo.extend(Roo.Layer, Roo.Element, {
23660
23661     getZIndex : function(){
23662         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23663     },
23664
23665     getShim : function(){
23666         if(!this.useShim){
23667             return null;
23668         }
23669         if(this.shim){
23670             return this.shim;
23671         }
23672         var shim = shims.shift();
23673         if(!shim){
23674             shim = this.createShim();
23675             shim.enableDisplayMode('block');
23676             shim.dom.style.display = 'none';
23677             shim.dom.style.visibility = 'visible';
23678         }
23679         var pn = this.dom.parentNode;
23680         if(shim.dom.parentNode != pn){
23681             pn.insertBefore(shim.dom, this.dom);
23682         }
23683         shim.setStyle('z-index', this.getZIndex()-2);
23684         this.shim = shim;
23685         return shim;
23686     },
23687
23688     hideShim : function(){
23689         if(this.shim){
23690             this.shim.setDisplayed(false);
23691             shims.push(this.shim);
23692             delete this.shim;
23693         }
23694     },
23695
23696     disableShadow : function(){
23697         if(this.shadow){
23698             this.shadowDisabled = true;
23699             this.shadow.hide();
23700             this.lastShadowOffset = this.shadowOffset;
23701             this.shadowOffset = 0;
23702         }
23703     },
23704
23705     enableShadow : function(show){
23706         if(this.shadow){
23707             this.shadowDisabled = false;
23708             this.shadowOffset = this.lastShadowOffset;
23709             delete this.lastShadowOffset;
23710             if(show){
23711                 this.sync(true);
23712             }
23713         }
23714     },
23715
23716     // private
23717     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23718     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23719     sync : function(doShow){
23720         var sw = this.shadow;
23721         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23722             var sh = this.getShim();
23723
23724             var w = this.getWidth(),
23725                 h = this.getHeight();
23726
23727             var l = this.getLeft(true),
23728                 t = this.getTop(true);
23729
23730             if(sw && !this.shadowDisabled){
23731                 if(doShow && !sw.isVisible()){
23732                     sw.show(this);
23733                 }else{
23734                     sw.realign(l, t, w, h);
23735                 }
23736                 if(sh){
23737                     if(doShow){
23738                        sh.show();
23739                     }
23740                     // fit the shim behind the shadow, so it is shimmed too
23741                     var a = sw.adjusts, s = sh.dom.style;
23742                     s.left = (Math.min(l, l+a.l))+"px";
23743                     s.top = (Math.min(t, t+a.t))+"px";
23744                     s.width = (w+a.w)+"px";
23745                     s.height = (h+a.h)+"px";
23746                 }
23747             }else if(sh){
23748                 if(doShow){
23749                    sh.show();
23750                 }
23751                 sh.setSize(w, h);
23752                 sh.setLeftTop(l, t);
23753             }
23754             
23755         }
23756     },
23757
23758     // private
23759     destroy : function(){
23760         this.hideShim();
23761         if(this.shadow){
23762             this.shadow.hide();
23763         }
23764         this.removeAllListeners();
23765         var pn = this.dom.parentNode;
23766         if(pn){
23767             pn.removeChild(this.dom);
23768         }
23769         Roo.Element.uncache(this.id);
23770     },
23771
23772     remove : function(){
23773         this.destroy();
23774     },
23775
23776     // private
23777     beginUpdate : function(){
23778         this.updating = true;
23779     },
23780
23781     // private
23782     endUpdate : function(){
23783         this.updating = false;
23784         this.sync(true);
23785     },
23786
23787     // private
23788     hideUnders : function(negOffset){
23789         if(this.shadow){
23790             this.shadow.hide();
23791         }
23792         this.hideShim();
23793     },
23794
23795     // private
23796     constrainXY : function(){
23797         if(this.constrain){
23798             var vw = Roo.lib.Dom.getViewWidth(),
23799                 vh = Roo.lib.Dom.getViewHeight();
23800             var s = Roo.get(document).getScroll();
23801
23802             var xy = this.getXY();
23803             var x = xy[0], y = xy[1];   
23804             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23805             // only move it if it needs it
23806             var moved = false;
23807             // first validate right/bottom
23808             if((x + w) > vw+s.left){
23809                 x = vw - w - this.shadowOffset;
23810                 moved = true;
23811             }
23812             if((y + h) > vh+s.top){
23813                 y = vh - h - this.shadowOffset;
23814                 moved = true;
23815             }
23816             // then make sure top/left isn't negative
23817             if(x < s.left){
23818                 x = s.left;
23819                 moved = true;
23820             }
23821             if(y < s.top){
23822                 y = s.top;
23823                 moved = true;
23824             }
23825             if(moved){
23826                 if(this.avoidY){
23827                     var ay = this.avoidY;
23828                     if(y <= ay && (y+h) >= ay){
23829                         y = ay-h-5;   
23830                     }
23831                 }
23832                 xy = [x, y];
23833                 this.storeXY(xy);
23834                 supr.setXY.call(this, xy);
23835                 this.sync();
23836             }
23837         }
23838     },
23839
23840     isVisible : function(){
23841         return this.visible;    
23842     },
23843
23844     // private
23845     showAction : function(){
23846         this.visible = true; // track visibility to prevent getStyle calls
23847         if(this.useDisplay === true){
23848             this.setDisplayed("");
23849         }else if(this.lastXY){
23850             supr.setXY.call(this, this.lastXY);
23851         }else if(this.lastLT){
23852             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23853         }
23854     },
23855
23856     // private
23857     hideAction : function(){
23858         this.visible = false;
23859         if(this.useDisplay === true){
23860             this.setDisplayed(false);
23861         }else{
23862             this.setLeftTop(-10000,-10000);
23863         }
23864     },
23865
23866     // overridden Element method
23867     setVisible : function(v, a, d, c, e){
23868         if(v){
23869             this.showAction();
23870         }
23871         if(a && v){
23872             var cb = function(){
23873                 this.sync(true);
23874                 if(c){
23875                     c();
23876                 }
23877             }.createDelegate(this);
23878             supr.setVisible.call(this, true, true, d, cb, e);
23879         }else{
23880             if(!v){
23881                 this.hideUnders(true);
23882             }
23883             var cb = c;
23884             if(a){
23885                 cb = function(){
23886                     this.hideAction();
23887                     if(c){
23888                         c();
23889                     }
23890                 }.createDelegate(this);
23891             }
23892             supr.setVisible.call(this, v, a, d, cb, e);
23893             if(v){
23894                 this.sync(true);
23895             }else if(!a){
23896                 this.hideAction();
23897             }
23898         }
23899     },
23900
23901     storeXY : function(xy){
23902         delete this.lastLT;
23903         this.lastXY = xy;
23904     },
23905
23906     storeLeftTop : function(left, top){
23907         delete this.lastXY;
23908         this.lastLT = [left, top];
23909     },
23910
23911     // private
23912     beforeFx : function(){
23913         this.beforeAction();
23914         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23915     },
23916
23917     // private
23918     afterFx : function(){
23919         Roo.Layer.superclass.afterFx.apply(this, arguments);
23920         this.sync(this.isVisible());
23921     },
23922
23923     // private
23924     beforeAction : function(){
23925         if(!this.updating && this.shadow){
23926             this.shadow.hide();
23927         }
23928     },
23929
23930     // overridden Element method
23931     setLeft : function(left){
23932         this.storeLeftTop(left, this.getTop(true));
23933         supr.setLeft.apply(this, arguments);
23934         this.sync();
23935     },
23936
23937     setTop : function(top){
23938         this.storeLeftTop(this.getLeft(true), top);
23939         supr.setTop.apply(this, arguments);
23940         this.sync();
23941     },
23942
23943     setLeftTop : function(left, top){
23944         this.storeLeftTop(left, top);
23945         supr.setLeftTop.apply(this, arguments);
23946         this.sync();
23947     },
23948
23949     setXY : function(xy, a, d, c, e){
23950         this.fixDisplay();
23951         this.beforeAction();
23952         this.storeXY(xy);
23953         var cb = this.createCB(c);
23954         supr.setXY.call(this, xy, a, d, cb, e);
23955         if(!a){
23956             cb();
23957         }
23958     },
23959
23960     // private
23961     createCB : function(c){
23962         var el = this;
23963         return function(){
23964             el.constrainXY();
23965             el.sync(true);
23966             if(c){
23967                 c();
23968             }
23969         };
23970     },
23971
23972     // overridden Element method
23973     setX : function(x, a, d, c, e){
23974         this.setXY([x, this.getY()], a, d, c, e);
23975     },
23976
23977     // overridden Element method
23978     setY : function(y, a, d, c, e){
23979         this.setXY([this.getX(), y], a, d, c, e);
23980     },
23981
23982     // overridden Element method
23983     setSize : function(w, h, a, d, c, e){
23984         this.beforeAction();
23985         var cb = this.createCB(c);
23986         supr.setSize.call(this, w, h, a, d, cb, e);
23987         if(!a){
23988             cb();
23989         }
23990     },
23991
23992     // overridden Element method
23993     setWidth : function(w, a, d, c, e){
23994         this.beforeAction();
23995         var cb = this.createCB(c);
23996         supr.setWidth.call(this, w, a, d, cb, e);
23997         if(!a){
23998             cb();
23999         }
24000     },
24001
24002     // overridden Element method
24003     setHeight : function(h, a, d, c, e){
24004         this.beforeAction();
24005         var cb = this.createCB(c);
24006         supr.setHeight.call(this, h, a, d, cb, e);
24007         if(!a){
24008             cb();
24009         }
24010     },
24011
24012     // overridden Element method
24013     setBounds : function(x, y, w, h, a, d, c, e){
24014         this.beforeAction();
24015         var cb = this.createCB(c);
24016         if(!a){
24017             this.storeXY([x, y]);
24018             supr.setXY.call(this, [x, y]);
24019             supr.setSize.call(this, w, h, a, d, cb, e);
24020             cb();
24021         }else{
24022             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24023         }
24024         return this;
24025     },
24026     
24027     /**
24028      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24029      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24030      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24031      * @param {Number} zindex The new z-index to set
24032      * @return {this} The Layer
24033      */
24034     setZIndex : function(zindex){
24035         this.zindex = zindex;
24036         this.setStyle("z-index", zindex + 2);
24037         if(this.shadow){
24038             this.shadow.setZIndex(zindex + 1);
24039         }
24040         if(this.shim){
24041             this.shim.setStyle("z-index", zindex);
24042         }
24043     }
24044 });
24045 })();/*
24046  * Based on:
24047  * Ext JS Library 1.1.1
24048  * Copyright(c) 2006-2007, Ext JS, LLC.
24049  *
24050  * Originally Released Under LGPL - original licence link has changed is not relivant.
24051  *
24052  * Fork - LGPL
24053  * <script type="text/javascript">
24054  */
24055
24056
24057 /**
24058  * @class Roo.Shadow
24059  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24060  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24061  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24062  * @constructor
24063  * Create a new Shadow
24064  * @param {Object} config The config object
24065  */
24066 Roo.Shadow = function(config){
24067     Roo.apply(this, config);
24068     if(typeof this.mode != "string"){
24069         this.mode = this.defaultMode;
24070     }
24071     var o = this.offset, a = {h: 0};
24072     var rad = Math.floor(this.offset/2);
24073     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24074         case "drop":
24075             a.w = 0;
24076             a.l = a.t = o;
24077             a.t -= 1;
24078             if(Roo.isIE){
24079                 a.l -= this.offset + rad;
24080                 a.t -= this.offset + rad;
24081                 a.w -= rad;
24082                 a.h -= rad;
24083                 a.t += 1;
24084             }
24085         break;
24086         case "sides":
24087             a.w = (o*2);
24088             a.l = -o;
24089             a.t = o-1;
24090             if(Roo.isIE){
24091                 a.l -= (this.offset - rad);
24092                 a.t -= this.offset + rad;
24093                 a.l += 1;
24094                 a.w -= (this.offset - rad)*2;
24095                 a.w -= rad + 1;
24096                 a.h -= 1;
24097             }
24098         break;
24099         case "frame":
24100             a.w = a.h = (o*2);
24101             a.l = a.t = -o;
24102             a.t += 1;
24103             a.h -= 2;
24104             if(Roo.isIE){
24105                 a.l -= (this.offset - rad);
24106                 a.t -= (this.offset - rad);
24107                 a.l += 1;
24108                 a.w -= (this.offset + rad + 1);
24109                 a.h -= (this.offset + rad);
24110                 a.h += 1;
24111             }
24112         break;
24113     };
24114
24115     this.adjusts = a;
24116 };
24117
24118 Roo.Shadow.prototype = {
24119     /**
24120      * @cfg {String} mode
24121      * The shadow display mode.  Supports the following options:<br />
24122      * sides: Shadow displays on both sides and bottom only<br />
24123      * frame: Shadow displays equally on all four sides<br />
24124      * drop: Traditional bottom-right drop shadow (default)
24125      */
24126     /**
24127      * @cfg {String} offset
24128      * The number of pixels to offset the shadow from the element (defaults to 4)
24129      */
24130     offset: 4,
24131
24132     // private
24133     defaultMode: "drop",
24134
24135     /**
24136      * Displays the shadow under the target element
24137      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24138      */
24139     show : function(target){
24140         target = Roo.get(target);
24141         if(!this.el){
24142             this.el = Roo.Shadow.Pool.pull();
24143             if(this.el.dom.nextSibling != target.dom){
24144                 this.el.insertBefore(target);
24145             }
24146         }
24147         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24148         if(Roo.isIE){
24149             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24150         }
24151         this.realign(
24152             target.getLeft(true),
24153             target.getTop(true),
24154             target.getWidth(),
24155             target.getHeight()
24156         );
24157         this.el.dom.style.display = "block";
24158     },
24159
24160     /**
24161      * Returns true if the shadow is visible, else false
24162      */
24163     isVisible : function(){
24164         return this.el ? true : false;  
24165     },
24166
24167     /**
24168      * Direct alignment when values are already available. Show must be called at least once before
24169      * calling this method to ensure it is initialized.
24170      * @param {Number} left The target element left position
24171      * @param {Number} top The target element top position
24172      * @param {Number} width The target element width
24173      * @param {Number} height The target element height
24174      */
24175     realign : function(l, t, w, h){
24176         if(!this.el){
24177             return;
24178         }
24179         var a = this.adjusts, d = this.el.dom, s = d.style;
24180         var iea = 0;
24181         s.left = (l+a.l)+"px";
24182         s.top = (t+a.t)+"px";
24183         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24184  
24185         if(s.width != sws || s.height != shs){
24186             s.width = sws;
24187             s.height = shs;
24188             if(!Roo.isIE){
24189                 var cn = d.childNodes;
24190                 var sww = Math.max(0, (sw-12))+"px";
24191                 cn[0].childNodes[1].style.width = sww;
24192                 cn[1].childNodes[1].style.width = sww;
24193                 cn[2].childNodes[1].style.width = sww;
24194                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24195             }
24196         }
24197     },
24198
24199     /**
24200      * Hides this shadow
24201      */
24202     hide : function(){
24203         if(this.el){
24204             this.el.dom.style.display = "none";
24205             Roo.Shadow.Pool.push(this.el);
24206             delete this.el;
24207         }
24208     },
24209
24210     /**
24211      * Adjust the z-index of this shadow
24212      * @param {Number} zindex The new z-index
24213      */
24214     setZIndex : function(z){
24215         this.zIndex = z;
24216         if(this.el){
24217             this.el.setStyle("z-index", z);
24218         }
24219     }
24220 };
24221
24222 // Private utility class that manages the internal Shadow cache
24223 Roo.Shadow.Pool = function(){
24224     var p = [];
24225     var markup = Roo.isIE ?
24226                  '<div class="x-ie-shadow"></div>' :
24227                  '<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>';
24228     return {
24229         pull : function(){
24230             var sh = p.shift();
24231             if(!sh){
24232                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24233                 sh.autoBoxAdjust = false;
24234             }
24235             return sh;
24236         },
24237
24238         push : function(sh){
24239             p.push(sh);
24240         }
24241     };
24242 }();/*
24243  * Based on:
24244  * Ext JS Library 1.1.1
24245  * Copyright(c) 2006-2007, Ext JS, LLC.
24246  *
24247  * Originally Released Under LGPL - original licence link has changed is not relivant.
24248  *
24249  * Fork - LGPL
24250  * <script type="text/javascript">
24251  */
24252
24253
24254 /**
24255  * @class Roo.SplitBar
24256  * @extends Roo.util.Observable
24257  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24258  * <br><br>
24259  * Usage:
24260  * <pre><code>
24261 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24262                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24263 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24264 split.minSize = 100;
24265 split.maxSize = 600;
24266 split.animate = true;
24267 split.on('moved', splitterMoved);
24268 </code></pre>
24269  * @constructor
24270  * Create a new SplitBar
24271  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24272  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24273  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24274  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24275                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24276                         position of the SplitBar).
24277  */
24278 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24279     
24280     /** @private */
24281     this.el = Roo.get(dragElement, true);
24282     this.el.dom.unselectable = "on";
24283     /** @private */
24284     this.resizingEl = Roo.get(resizingElement, true);
24285
24286     /**
24287      * @private
24288      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24289      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24290      * @type Number
24291      */
24292     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24293     
24294     /**
24295      * The minimum size of the resizing element. (Defaults to 0)
24296      * @type Number
24297      */
24298     this.minSize = 0;
24299     
24300     /**
24301      * The maximum size of the resizing element. (Defaults to 2000)
24302      * @type Number
24303      */
24304     this.maxSize = 2000;
24305     
24306     /**
24307      * Whether to animate the transition to the new size
24308      * @type Boolean
24309      */
24310     this.animate = false;
24311     
24312     /**
24313      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24314      * @type Boolean
24315      */
24316     this.useShim = false;
24317     
24318     /** @private */
24319     this.shim = null;
24320     
24321     if(!existingProxy){
24322         /** @private */
24323         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24324     }else{
24325         this.proxy = Roo.get(existingProxy).dom;
24326     }
24327     /** @private */
24328     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24329     
24330     /** @private */
24331     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24332     
24333     /** @private */
24334     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24335     
24336     /** @private */
24337     this.dragSpecs = {};
24338     
24339     /**
24340      * @private The adapter to use to positon and resize elements
24341      */
24342     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24343     this.adapter.init(this);
24344     
24345     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24346         /** @private */
24347         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24348         this.el.addClass("x-splitbar-h");
24349     }else{
24350         /** @private */
24351         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24352         this.el.addClass("x-splitbar-v");
24353     }
24354     
24355     this.addEvents({
24356         /**
24357          * @event resize
24358          * Fires when the splitter is moved (alias for {@link #event-moved})
24359          * @param {Roo.SplitBar} this
24360          * @param {Number} newSize the new width or height
24361          */
24362         "resize" : true,
24363         /**
24364          * @event moved
24365          * Fires when the splitter is moved
24366          * @param {Roo.SplitBar} this
24367          * @param {Number} newSize the new width or height
24368          */
24369         "moved" : true,
24370         /**
24371          * @event beforeresize
24372          * Fires before the splitter is dragged
24373          * @param {Roo.SplitBar} this
24374          */
24375         "beforeresize" : true,
24376
24377         "beforeapply" : true
24378     });
24379
24380     Roo.util.Observable.call(this);
24381 };
24382
24383 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24384     onStartProxyDrag : function(x, y){
24385         this.fireEvent("beforeresize", this);
24386         if(!this.overlay){
24387             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24388             o.unselectable();
24389             o.enableDisplayMode("block");
24390             // all splitbars share the same overlay
24391             Roo.SplitBar.prototype.overlay = o;
24392         }
24393         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24394         this.overlay.show();
24395         Roo.get(this.proxy).setDisplayed("block");
24396         var size = this.adapter.getElementSize(this);
24397         this.activeMinSize = this.getMinimumSize();;
24398         this.activeMaxSize = this.getMaximumSize();;
24399         var c1 = size - this.activeMinSize;
24400         var c2 = Math.max(this.activeMaxSize - size, 0);
24401         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24402             this.dd.resetConstraints();
24403             this.dd.setXConstraint(
24404                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24405                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24406             );
24407             this.dd.setYConstraint(0, 0);
24408         }else{
24409             this.dd.resetConstraints();
24410             this.dd.setXConstraint(0, 0);
24411             this.dd.setYConstraint(
24412                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24413                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24414             );
24415          }
24416         this.dragSpecs.startSize = size;
24417         this.dragSpecs.startPoint = [x, y];
24418         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24419     },
24420     
24421     /** 
24422      * @private Called after the drag operation by the DDProxy
24423      */
24424     onEndProxyDrag : function(e){
24425         Roo.get(this.proxy).setDisplayed(false);
24426         var endPoint = Roo.lib.Event.getXY(e);
24427         if(this.overlay){
24428             this.overlay.hide();
24429         }
24430         var newSize;
24431         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24432             newSize = this.dragSpecs.startSize + 
24433                 (this.placement == Roo.SplitBar.LEFT ?
24434                     endPoint[0] - this.dragSpecs.startPoint[0] :
24435                     this.dragSpecs.startPoint[0] - endPoint[0]
24436                 );
24437         }else{
24438             newSize = this.dragSpecs.startSize + 
24439                 (this.placement == Roo.SplitBar.TOP ?
24440                     endPoint[1] - this.dragSpecs.startPoint[1] :
24441                     this.dragSpecs.startPoint[1] - endPoint[1]
24442                 );
24443         }
24444         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24445         if(newSize != this.dragSpecs.startSize){
24446             if(this.fireEvent('beforeapply', this, newSize) !== false){
24447                 this.adapter.setElementSize(this, newSize);
24448                 this.fireEvent("moved", this, newSize);
24449                 this.fireEvent("resize", this, newSize);
24450             }
24451         }
24452     },
24453     
24454     /**
24455      * Get the adapter this SplitBar uses
24456      * @return The adapter object
24457      */
24458     getAdapter : function(){
24459         return this.adapter;
24460     },
24461     
24462     /**
24463      * Set the adapter this SplitBar uses
24464      * @param {Object} adapter A SplitBar adapter object
24465      */
24466     setAdapter : function(adapter){
24467         this.adapter = adapter;
24468         this.adapter.init(this);
24469     },
24470     
24471     /**
24472      * Gets the minimum size for the resizing element
24473      * @return {Number} The minimum size
24474      */
24475     getMinimumSize : function(){
24476         return this.minSize;
24477     },
24478     
24479     /**
24480      * Sets the minimum size for the resizing element
24481      * @param {Number} minSize The minimum size
24482      */
24483     setMinimumSize : function(minSize){
24484         this.minSize = minSize;
24485     },
24486     
24487     /**
24488      * Gets the maximum size for the resizing element
24489      * @return {Number} The maximum size
24490      */
24491     getMaximumSize : function(){
24492         return this.maxSize;
24493     },
24494     
24495     /**
24496      * Sets the maximum size for the resizing element
24497      * @param {Number} maxSize The maximum size
24498      */
24499     setMaximumSize : function(maxSize){
24500         this.maxSize = maxSize;
24501     },
24502     
24503     /**
24504      * Sets the initialize size for the resizing element
24505      * @param {Number} size The initial size
24506      */
24507     setCurrentSize : function(size){
24508         var oldAnimate = this.animate;
24509         this.animate = false;
24510         this.adapter.setElementSize(this, size);
24511         this.animate = oldAnimate;
24512     },
24513     
24514     /**
24515      * Destroy this splitbar. 
24516      * @param {Boolean} removeEl True to remove the element
24517      */
24518     destroy : function(removeEl){
24519         if(this.shim){
24520             this.shim.remove();
24521         }
24522         this.dd.unreg();
24523         this.proxy.parentNode.removeChild(this.proxy);
24524         if(removeEl){
24525             this.el.remove();
24526         }
24527     }
24528 });
24529
24530 /**
24531  * @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.
24532  */
24533 Roo.SplitBar.createProxy = function(dir){
24534     var proxy = new Roo.Element(document.createElement("div"));
24535     proxy.unselectable();
24536     var cls = 'x-splitbar-proxy';
24537     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24538     document.body.appendChild(proxy.dom);
24539     return proxy.dom;
24540 };
24541
24542 /** 
24543  * @class Roo.SplitBar.BasicLayoutAdapter
24544  * Default Adapter. It assumes the splitter and resizing element are not positioned
24545  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24546  */
24547 Roo.SplitBar.BasicLayoutAdapter = function(){
24548 };
24549
24550 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24551     // do nothing for now
24552     init : function(s){
24553     
24554     },
24555     /**
24556      * Called before drag operations to get the current size of the resizing element. 
24557      * @param {Roo.SplitBar} s The SplitBar using this adapter
24558      */
24559      getElementSize : function(s){
24560         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24561             return s.resizingEl.getWidth();
24562         }else{
24563             return s.resizingEl.getHeight();
24564         }
24565     },
24566     
24567     /**
24568      * Called after drag operations to set the size of the resizing element.
24569      * @param {Roo.SplitBar} s The SplitBar using this adapter
24570      * @param {Number} newSize The new size to set
24571      * @param {Function} onComplete A function to be invoked when resizing is complete
24572      */
24573     setElementSize : function(s, newSize, onComplete){
24574         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24575             if(!s.animate){
24576                 s.resizingEl.setWidth(newSize);
24577                 if(onComplete){
24578                     onComplete(s, newSize);
24579                 }
24580             }else{
24581                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24582             }
24583         }else{
24584             
24585             if(!s.animate){
24586                 s.resizingEl.setHeight(newSize);
24587                 if(onComplete){
24588                     onComplete(s, newSize);
24589                 }
24590             }else{
24591                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24592             }
24593         }
24594     }
24595 };
24596
24597 /** 
24598  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24599  * @extends Roo.SplitBar.BasicLayoutAdapter
24600  * Adapter that  moves the splitter element to align with the resized sizing element. 
24601  * Used with an absolute positioned SplitBar.
24602  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24603  * document.body, make sure you assign an id to the body element.
24604  */
24605 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24606     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24607     this.container = Roo.get(container);
24608 };
24609
24610 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24611     init : function(s){
24612         this.basic.init(s);
24613     },
24614     
24615     getElementSize : function(s){
24616         return this.basic.getElementSize(s);
24617     },
24618     
24619     setElementSize : function(s, newSize, onComplete){
24620         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24621     },
24622     
24623     moveSplitter : function(s){
24624         var yes = Roo.SplitBar;
24625         switch(s.placement){
24626             case yes.LEFT:
24627                 s.el.setX(s.resizingEl.getRight());
24628                 break;
24629             case yes.RIGHT:
24630                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24631                 break;
24632             case yes.TOP:
24633                 s.el.setY(s.resizingEl.getBottom());
24634                 break;
24635             case yes.BOTTOM:
24636                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24637                 break;
24638         }
24639     }
24640 };
24641
24642 /**
24643  * Orientation constant - Create a vertical SplitBar
24644  * @static
24645  * @type Number
24646  */
24647 Roo.SplitBar.VERTICAL = 1;
24648
24649 /**
24650  * Orientation constant - Create a horizontal SplitBar
24651  * @static
24652  * @type Number
24653  */
24654 Roo.SplitBar.HORIZONTAL = 2;
24655
24656 /**
24657  * Placement constant - The resizing element is to the left of the splitter element
24658  * @static
24659  * @type Number
24660  */
24661 Roo.SplitBar.LEFT = 1;
24662
24663 /**
24664  * Placement constant - The resizing element is to the right of the splitter element
24665  * @static
24666  * @type Number
24667  */
24668 Roo.SplitBar.RIGHT = 2;
24669
24670 /**
24671  * Placement constant - The resizing element is positioned above the splitter element
24672  * @static
24673  * @type Number
24674  */
24675 Roo.SplitBar.TOP = 3;
24676
24677 /**
24678  * Placement constant - The resizing element is positioned under splitter element
24679  * @static
24680  * @type Number
24681  */
24682 Roo.SplitBar.BOTTOM = 4;
24683 /*
24684  * Based on:
24685  * Ext JS Library 1.1.1
24686  * Copyright(c) 2006-2007, Ext JS, LLC.
24687  *
24688  * Originally Released Under LGPL - original licence link has changed is not relivant.
24689  *
24690  * Fork - LGPL
24691  * <script type="text/javascript">
24692  */
24693
24694 /**
24695  * @class Roo.View
24696  * @extends Roo.util.Observable
24697  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24698  * This class also supports single and multi selection modes. <br>
24699  * Create a data model bound view:
24700  <pre><code>
24701  var store = new Roo.data.Store(...);
24702
24703  var view = new Roo.View({
24704     el : "my-element",
24705     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24706  
24707     singleSelect: true,
24708     selectedClass: "ydataview-selected",
24709     store: store
24710  });
24711
24712  // listen for node click?
24713  view.on("click", function(vw, index, node, e){
24714  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24715  });
24716
24717  // load XML data
24718  dataModel.load("foobar.xml");
24719  </code></pre>
24720  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24721  * <br><br>
24722  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24723  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24724  * 
24725  * Note: old style constructor is still suported (container, template, config)
24726  * 
24727  * @constructor
24728  * Create a new View
24729  * @param {Object} config The config object
24730  * 
24731  */
24732 Roo.View = function(config, depreciated_tpl, depreciated_config){
24733     
24734     if (typeof(depreciated_tpl) == 'undefined') {
24735         // new way.. - universal constructor.
24736         Roo.apply(this, config);
24737         this.el  = Roo.get(this.el);
24738     } else {
24739         // old format..
24740         this.el  = Roo.get(config);
24741         this.tpl = depreciated_tpl;
24742         Roo.apply(this, depreciated_config);
24743     }
24744     this.wrapEl  = this.el.wrap().wrap();
24745     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24746     
24747     
24748     if(typeof(this.tpl) == "string"){
24749         this.tpl = new Roo.Template(this.tpl);
24750     } else {
24751         // support xtype ctors..
24752         this.tpl = new Roo.factory(this.tpl, Roo);
24753     }
24754     
24755     
24756     this.tpl.compile();
24757    
24758   
24759     
24760      
24761     /** @private */
24762     this.addEvents({
24763         /**
24764          * @event beforeclick
24765          * Fires before a click is processed. Returns false to cancel the default action.
24766          * @param {Roo.View} this
24767          * @param {Number} index The index of the target node
24768          * @param {HTMLElement} node The target node
24769          * @param {Roo.EventObject} e The raw event object
24770          */
24771             "beforeclick" : true,
24772         /**
24773          * @event click
24774          * Fires when a template node is clicked.
24775          * @param {Roo.View} this
24776          * @param {Number} index The index of the target node
24777          * @param {HTMLElement} node The target node
24778          * @param {Roo.EventObject} e The raw event object
24779          */
24780             "click" : true,
24781         /**
24782          * @event dblclick
24783          * Fires when a template node is double clicked.
24784          * @param {Roo.View} this
24785          * @param {Number} index The index of the target node
24786          * @param {HTMLElement} node The target node
24787          * @param {Roo.EventObject} e The raw event object
24788          */
24789             "dblclick" : true,
24790         /**
24791          * @event contextmenu
24792          * Fires when a template node is right clicked.
24793          * @param {Roo.View} this
24794          * @param {Number} index The index of the target node
24795          * @param {HTMLElement} node The target node
24796          * @param {Roo.EventObject} e The raw event object
24797          */
24798             "contextmenu" : true,
24799         /**
24800          * @event selectionchange
24801          * Fires when the selected nodes change.
24802          * @param {Roo.View} this
24803          * @param {Array} selections Array of the selected nodes
24804          */
24805             "selectionchange" : true,
24806     
24807         /**
24808          * @event beforeselect
24809          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24810          * @param {Roo.View} this
24811          * @param {HTMLElement} node The node to be selected
24812          * @param {Array} selections Array of currently selected nodes
24813          */
24814             "beforeselect" : true,
24815         /**
24816          * @event preparedata
24817          * Fires on every row to render, to allow you to change the data.
24818          * @param {Roo.View} this
24819          * @param {Object} data to be rendered (change this)
24820          */
24821           "preparedata" : true
24822           
24823           
24824         });
24825
24826
24827
24828     this.el.on({
24829         "click": this.onClick,
24830         "dblclick": this.onDblClick,
24831         "contextmenu": this.onContextMenu,
24832         scope:this
24833     });
24834
24835     this.selections = [];
24836     this.nodes = [];
24837     this.cmp = new Roo.CompositeElementLite([]);
24838     if(this.store){
24839         this.store = Roo.factory(this.store, Roo.data);
24840         this.setStore(this.store, true);
24841     }
24842     
24843     if ( this.footer && this.footer.xtype) {
24844            
24845          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24846         
24847         this.footer.dataSource = this.store
24848         this.footer.container = fctr;
24849         this.footer = Roo.factory(this.footer, Roo);
24850         fctr.insertFirst(this.el);
24851         
24852         // this is a bit insane - as the paging toolbar seems to detach the el..
24853 //        dom.parentNode.parentNode.parentNode
24854          // they get detached?
24855     }
24856     
24857     
24858     Roo.View.superclass.constructor.call(this);
24859     
24860     
24861 };
24862
24863 Roo.extend(Roo.View, Roo.util.Observable, {
24864     
24865      /**
24866      * @cfg {Roo.data.Store} store Data store to load data from.
24867      */
24868     store : false,
24869     
24870     /**
24871      * @cfg {String|Roo.Element} el The container element.
24872      */
24873     el : '',
24874     
24875     /**
24876      * @cfg {String|Roo.Template} tpl The template used by this View 
24877      */
24878     tpl : false,
24879     /**
24880      * @cfg {String} dataName the named area of the template to use as the data area
24881      *                          Works with domtemplates roo-name="name"
24882      */
24883     dataName: false,
24884     /**
24885      * @cfg {String} selectedClass The css class to add to selected nodes
24886      */
24887     selectedClass : "x-view-selected",
24888      /**
24889      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24890      */
24891     emptyText : "",
24892     
24893     /**
24894      * @cfg {String} text to display on mask (default Loading)
24895      */
24896     mask : false,
24897     /**
24898      * @cfg {Boolean} multiSelect Allow multiple selection
24899      */
24900     multiSelect : false,
24901     /**
24902      * @cfg {Boolean} singleSelect Allow single selection
24903      */
24904     singleSelect:  false,
24905     
24906     /**
24907      * @cfg {Boolean} toggleSelect - selecting 
24908      */
24909     toggleSelect : false,
24910     
24911     /**
24912      * Returns the element this view is bound to.
24913      * @return {Roo.Element}
24914      */
24915     getEl : function(){
24916         return this.wrapEl;
24917     },
24918     
24919     
24920
24921     /**
24922      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24923      */
24924     refresh : function(){
24925         var t = this.tpl;
24926         
24927         // if we are using something like 'domtemplate', then
24928         // the what gets used is:
24929         // t.applySubtemplate(NAME, data, wrapping data..)
24930         // the outer template then get' applied with
24931         //     the store 'extra data'
24932         // and the body get's added to the
24933         //      roo-name="data" node?
24934         //      <span class='roo-tpl-{name}'></span> ?????
24935         
24936         
24937         
24938         this.clearSelections();
24939         this.el.update("");
24940         var html = [];
24941         var records = this.store.getRange();
24942         if(records.length < 1) {
24943             
24944             // is this valid??  = should it render a template??
24945             
24946             this.el.update(this.emptyText);
24947             return;
24948         }
24949         var el = this.el;
24950         if (this.dataName) {
24951             this.el.update(t.apply(this.store.meta)); //????
24952             el = this.el.child('.roo-tpl-' + this.dataName);
24953         }
24954         
24955         for(var i = 0, len = records.length; i < len; i++){
24956             var data = this.prepareData(records[i].data, i, records[i]);
24957             this.fireEvent("preparedata", this, data, i, records[i]);
24958             html[html.length] = Roo.util.Format.trim(
24959                 this.dataName ?
24960                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24961                     t.apply(data)
24962             );
24963         }
24964         
24965         
24966         
24967         el.update(html.join(""));
24968         this.nodes = el.dom.childNodes;
24969         this.updateIndexes(0);
24970     },
24971
24972     /**
24973      * Function to override to reformat the data that is sent to
24974      * the template for each node.
24975      * DEPRICATED - use the preparedata event handler.
24976      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24977      * a JSON object for an UpdateManager bound view).
24978      */
24979     prepareData : function(data, index, record)
24980     {
24981         this.fireEvent("preparedata", this, data, index, record);
24982         return data;
24983     },
24984
24985     onUpdate : function(ds, record){
24986         this.clearSelections();
24987         var index = this.store.indexOf(record);
24988         var n = this.nodes[index];
24989         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24990         n.parentNode.removeChild(n);
24991         this.updateIndexes(index, index);
24992     },
24993
24994     
24995     
24996 // --------- FIXME     
24997     onAdd : function(ds, records, index)
24998     {
24999         this.clearSelections();
25000         if(this.nodes.length == 0){
25001             this.refresh();
25002             return;
25003         }
25004         var n = this.nodes[index];
25005         for(var i = 0, len = records.length; i < len; i++){
25006             var d = this.prepareData(records[i].data, i, records[i]);
25007             if(n){
25008                 this.tpl.insertBefore(n, d);
25009             }else{
25010                 
25011                 this.tpl.append(this.el, d);
25012             }
25013         }
25014         this.updateIndexes(index);
25015     },
25016
25017     onRemove : function(ds, record, index){
25018         this.clearSelections();
25019         var el = this.dataName  ?
25020             this.el.child('.roo-tpl-' + this.dataName) :
25021             this.el; 
25022         el.dom.removeChild(this.nodes[index]);
25023         this.updateIndexes(index);
25024     },
25025
25026     /**
25027      * Refresh an individual node.
25028      * @param {Number} index
25029      */
25030     refreshNode : function(index){
25031         this.onUpdate(this.store, this.store.getAt(index));
25032     },
25033
25034     updateIndexes : function(startIndex, endIndex){
25035         var ns = this.nodes;
25036         startIndex = startIndex || 0;
25037         endIndex = endIndex || ns.length - 1;
25038         for(var i = startIndex; i <= endIndex; i++){
25039             ns[i].nodeIndex = i;
25040         }
25041     },
25042
25043     /**
25044      * Changes the data store this view uses and refresh the view.
25045      * @param {Store} store
25046      */
25047     setStore : function(store, initial){
25048         if(!initial && this.store){
25049             this.store.un("datachanged", this.refresh);
25050             this.store.un("add", this.onAdd);
25051             this.store.un("remove", this.onRemove);
25052             this.store.un("update", this.onUpdate);
25053             this.store.un("clear", this.refresh);
25054             this.store.un("beforeload", this.onBeforeLoad);
25055             this.store.un("load", this.onLoad);
25056             this.store.un("loadexception", this.onLoad);
25057         }
25058         if(store){
25059           
25060             store.on("datachanged", this.refresh, this);
25061             store.on("add", this.onAdd, this);
25062             store.on("remove", this.onRemove, this);
25063             store.on("update", this.onUpdate, this);
25064             store.on("clear", this.refresh, this);
25065             store.on("beforeload", this.onBeforeLoad, this);
25066             store.on("load", this.onLoad, this);
25067             store.on("loadexception", this.onLoad, this);
25068         }
25069         
25070         if(store){
25071             this.refresh();
25072         }
25073     },
25074     /**
25075      * onbeforeLoad - masks the loading area.
25076      *
25077      */
25078     onBeforeLoad : function()
25079     {
25080         this.el.update("");
25081         this.el.mask(this.mask ? this.mask : "Loading" ); 
25082     },
25083     onLoad : function ()
25084     {
25085         this.el.unmask();
25086     },
25087     
25088
25089     /**
25090      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25091      * @param {HTMLElement} node
25092      * @return {HTMLElement} The template node
25093      */
25094     findItemFromChild : function(node){
25095         var el = this.dataName  ?
25096             this.el.child('.roo-tpl-' + this.dataName,true) :
25097             this.el.dom; 
25098         
25099         if(!node || node.parentNode == el){
25100                     return node;
25101             }
25102             var p = node.parentNode;
25103             while(p && p != el){
25104             if(p.parentNode == el){
25105                 return p;
25106             }
25107             p = p.parentNode;
25108         }
25109             return null;
25110     },
25111
25112     /** @ignore */
25113     onClick : function(e){
25114         var item = this.findItemFromChild(e.getTarget());
25115         if(item){
25116             var index = this.indexOf(item);
25117             if(this.onItemClick(item, index, e) !== false){
25118                 this.fireEvent("click", this, index, item, e);
25119             }
25120         }else{
25121             this.clearSelections();
25122         }
25123     },
25124
25125     /** @ignore */
25126     onContextMenu : function(e){
25127         var item = this.findItemFromChild(e.getTarget());
25128         if(item){
25129             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25130         }
25131     },
25132
25133     /** @ignore */
25134     onDblClick : function(e){
25135         var item = this.findItemFromChild(e.getTarget());
25136         if(item){
25137             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25138         }
25139     },
25140
25141     onItemClick : function(item, index, e)
25142     {
25143         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25144             return false;
25145         }
25146         if (this.toggleSelect) {
25147             var m = this.isSelected(item) ? 'unselect' : 'select';
25148             Roo.log(m);
25149             var _t = this;
25150             _t[m](item, true, false);
25151             return true;
25152         }
25153         if(this.multiSelect || this.singleSelect){
25154             if(this.multiSelect && e.shiftKey && this.lastSelection){
25155                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25156             }else{
25157                 this.select(item, this.multiSelect && e.ctrlKey);
25158                 this.lastSelection = item;
25159             }
25160             e.preventDefault();
25161         }
25162         return true;
25163     },
25164
25165     /**
25166      * Get the number of selected nodes.
25167      * @return {Number}
25168      */
25169     getSelectionCount : function(){
25170         return this.selections.length;
25171     },
25172
25173     /**
25174      * Get the currently selected nodes.
25175      * @return {Array} An array of HTMLElements
25176      */
25177     getSelectedNodes : function(){
25178         return this.selections;
25179     },
25180
25181     /**
25182      * Get the indexes of the selected nodes.
25183      * @return {Array}
25184      */
25185     getSelectedIndexes : function(){
25186         var indexes = [], s = this.selections;
25187         for(var i = 0, len = s.length; i < len; i++){
25188             indexes.push(s[i].nodeIndex);
25189         }
25190         return indexes;
25191     },
25192
25193     /**
25194      * Clear all selections
25195      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25196      */
25197     clearSelections : function(suppressEvent){
25198         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25199             this.cmp.elements = this.selections;
25200             this.cmp.removeClass(this.selectedClass);
25201             this.selections = [];
25202             if(!suppressEvent){
25203                 this.fireEvent("selectionchange", this, this.selections);
25204             }
25205         }
25206     },
25207
25208     /**
25209      * Returns true if the passed node is selected
25210      * @param {HTMLElement/Number} node The node or node index
25211      * @return {Boolean}
25212      */
25213     isSelected : function(node){
25214         var s = this.selections;
25215         if(s.length < 1){
25216             return false;
25217         }
25218         node = this.getNode(node);
25219         return s.indexOf(node) !== -1;
25220     },
25221
25222     /**
25223      * Selects nodes.
25224      * @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
25225      * @param {Boolean} keepExisting (optional) true to keep existing selections
25226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25227      */
25228     select : function(nodeInfo, keepExisting, suppressEvent){
25229         if(nodeInfo instanceof Array){
25230             if(!keepExisting){
25231                 this.clearSelections(true);
25232             }
25233             for(var i = 0, len = nodeInfo.length; i < len; i++){
25234                 this.select(nodeInfo[i], true, true);
25235             }
25236             return;
25237         } 
25238         var node = this.getNode(nodeInfo);
25239         if(!node || this.isSelected(node)){
25240             return; // already selected.
25241         }
25242         if(!keepExisting){
25243             this.clearSelections(true);
25244         }
25245         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25246             Roo.fly(node).addClass(this.selectedClass);
25247             this.selections.push(node);
25248             if(!suppressEvent){
25249                 this.fireEvent("selectionchange", this, this.selections);
25250             }
25251         }
25252         
25253         
25254     },
25255       /**
25256      * Unselects nodes.
25257      * @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
25258      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25259      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25260      */
25261     unselect : function(nodeInfo, keepExisting, suppressEvent)
25262     {
25263         if(nodeInfo instanceof Array){
25264             Roo.each(this.selections, function(s) {
25265                 this.unselect(s, nodeInfo);
25266             }, this);
25267             return;
25268         }
25269         var node = this.getNode(nodeInfo);
25270         if(!node || !this.isSelected(node)){
25271             Roo.log("not selected");
25272             return; // not selected.
25273         }
25274         // fireevent???
25275         var ns = [];
25276         Roo.each(this.selections, function(s) {
25277             if (s == node ) {
25278                 Roo.fly(node).removeClass(this.selectedClass);
25279
25280                 return;
25281             }
25282             ns.push(s);
25283         },this);
25284         
25285         this.selections= ns;
25286         this.fireEvent("selectionchange", this, this.selections);
25287     },
25288
25289     /**
25290      * Gets a template node.
25291      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25292      * @return {HTMLElement} The node or null if it wasn't found
25293      */
25294     getNode : function(nodeInfo){
25295         if(typeof nodeInfo == "string"){
25296             return document.getElementById(nodeInfo);
25297         }else if(typeof nodeInfo == "number"){
25298             return this.nodes[nodeInfo];
25299         }
25300         return nodeInfo;
25301     },
25302
25303     /**
25304      * Gets a range template nodes.
25305      * @param {Number} startIndex
25306      * @param {Number} endIndex
25307      * @return {Array} An array of nodes
25308      */
25309     getNodes : function(start, end){
25310         var ns = this.nodes;
25311         start = start || 0;
25312         end = typeof end == "undefined" ? ns.length - 1 : end;
25313         var nodes = [];
25314         if(start <= end){
25315             for(var i = start; i <= end; i++){
25316                 nodes.push(ns[i]);
25317             }
25318         } else{
25319             for(var i = start; i >= end; i--){
25320                 nodes.push(ns[i]);
25321             }
25322         }
25323         return nodes;
25324     },
25325
25326     /**
25327      * Finds the index of the passed node
25328      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25329      * @return {Number} The index of the node or -1
25330      */
25331     indexOf : function(node){
25332         node = this.getNode(node);
25333         if(typeof node.nodeIndex == "number"){
25334             return node.nodeIndex;
25335         }
25336         var ns = this.nodes;
25337         for(var i = 0, len = ns.length; i < len; i++){
25338             if(ns[i] == node){
25339                 return i;
25340             }
25341         }
25342         return -1;
25343     }
25344 });
25345 /*
25346  * Based on:
25347  * Ext JS Library 1.1.1
25348  * Copyright(c) 2006-2007, Ext JS, LLC.
25349  *
25350  * Originally Released Under LGPL - original licence link has changed is not relivant.
25351  *
25352  * Fork - LGPL
25353  * <script type="text/javascript">
25354  */
25355
25356 /**
25357  * @class Roo.JsonView
25358  * @extends Roo.View
25359  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25360 <pre><code>
25361 var view = new Roo.JsonView({
25362     container: "my-element",
25363     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25364     multiSelect: true, 
25365     jsonRoot: "data" 
25366 });
25367
25368 // listen for node click?
25369 view.on("click", function(vw, index, node, e){
25370     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25371 });
25372
25373 // direct load of JSON data
25374 view.load("foobar.php");
25375
25376 // Example from my blog list
25377 var tpl = new Roo.Template(
25378     '&lt;div class="entry"&gt;' +
25379     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25380     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25381     "&lt;/div&gt;&lt;hr /&gt;"
25382 );
25383
25384 var moreView = new Roo.JsonView({
25385     container :  "entry-list", 
25386     template : tpl,
25387     jsonRoot: "posts"
25388 });
25389 moreView.on("beforerender", this.sortEntries, this);
25390 moreView.load({
25391     url: "/blog/get-posts.php",
25392     params: "allposts=true",
25393     text: "Loading Blog Entries..."
25394 });
25395 </code></pre>
25396
25397 * Note: old code is supported with arguments : (container, template, config)
25398
25399
25400  * @constructor
25401  * Create a new JsonView
25402  * 
25403  * @param {Object} config The config object
25404  * 
25405  */
25406 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25407     
25408     
25409     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25410
25411     var um = this.el.getUpdateManager();
25412     um.setRenderer(this);
25413     um.on("update", this.onLoad, this);
25414     um.on("failure", this.onLoadException, this);
25415
25416     /**
25417      * @event beforerender
25418      * Fires before rendering of the downloaded JSON data.
25419      * @param {Roo.JsonView} this
25420      * @param {Object} data The JSON data loaded
25421      */
25422     /**
25423      * @event load
25424      * Fires when data is loaded.
25425      * @param {Roo.JsonView} this
25426      * @param {Object} data The JSON data loaded
25427      * @param {Object} response The raw Connect response object
25428      */
25429     /**
25430      * @event loadexception
25431      * Fires when loading fails.
25432      * @param {Roo.JsonView} this
25433      * @param {Object} response The raw Connect response object
25434      */
25435     this.addEvents({
25436         'beforerender' : true,
25437         'load' : true,
25438         'loadexception' : true
25439     });
25440 };
25441 Roo.extend(Roo.JsonView, Roo.View, {
25442     /**
25443      * @type {String} The root property in the loaded JSON object that contains the data
25444      */
25445     jsonRoot : "",
25446
25447     /**
25448      * Refreshes the view.
25449      */
25450     refresh : function(){
25451         this.clearSelections();
25452         this.el.update("");
25453         var html = [];
25454         var o = this.jsonData;
25455         if(o && o.length > 0){
25456             for(var i = 0, len = o.length; i < len; i++){
25457                 var data = this.prepareData(o[i], i, o);
25458                 html[html.length] = this.tpl.apply(data);
25459             }
25460         }else{
25461             html.push(this.emptyText);
25462         }
25463         this.el.update(html.join(""));
25464         this.nodes = this.el.dom.childNodes;
25465         this.updateIndexes(0);
25466     },
25467
25468     /**
25469      * 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.
25470      * @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:
25471      <pre><code>
25472      view.load({
25473          url: "your-url.php",
25474          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25475          callback: yourFunction,
25476          scope: yourObject, //(optional scope)
25477          discardUrl: false,
25478          nocache: false,
25479          text: "Loading...",
25480          timeout: 30,
25481          scripts: false
25482      });
25483      </code></pre>
25484      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25485      * 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.
25486      * @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}
25487      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25488      * @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.
25489      */
25490     load : function(){
25491         var um = this.el.getUpdateManager();
25492         um.update.apply(um, arguments);
25493     },
25494
25495     render : function(el, response){
25496         this.clearSelections();
25497         this.el.update("");
25498         var o;
25499         try{
25500             o = Roo.util.JSON.decode(response.responseText);
25501             if(this.jsonRoot){
25502                 
25503                 o = o[this.jsonRoot];
25504             }
25505         } catch(e){
25506         }
25507         /**
25508          * The current JSON data or null
25509          */
25510         this.jsonData = o;
25511         this.beforeRender();
25512         this.refresh();
25513     },
25514
25515 /**
25516  * Get the number of records in the current JSON dataset
25517  * @return {Number}
25518  */
25519     getCount : function(){
25520         return this.jsonData ? this.jsonData.length : 0;
25521     },
25522
25523 /**
25524  * Returns the JSON object for the specified node(s)
25525  * @param {HTMLElement/Array} node The node or an array of nodes
25526  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25527  * you get the JSON object for the node
25528  */
25529     getNodeData : function(node){
25530         if(node instanceof Array){
25531             var data = [];
25532             for(var i = 0, len = node.length; i < len; i++){
25533                 data.push(this.getNodeData(node[i]));
25534             }
25535             return data;
25536         }
25537         return this.jsonData[this.indexOf(node)] || null;
25538     },
25539
25540     beforeRender : function(){
25541         this.snapshot = this.jsonData;
25542         if(this.sortInfo){
25543             this.sort.apply(this, this.sortInfo);
25544         }
25545         this.fireEvent("beforerender", this, this.jsonData);
25546     },
25547
25548     onLoad : function(el, o){
25549         this.fireEvent("load", this, this.jsonData, o);
25550     },
25551
25552     onLoadException : function(el, o){
25553         this.fireEvent("loadexception", this, o);
25554     },
25555
25556 /**
25557  * Filter the data by a specific property.
25558  * @param {String} property A property on your JSON objects
25559  * @param {String/RegExp} value Either string that the property values
25560  * should start with, or a RegExp to test against the property
25561  */
25562     filter : function(property, value){
25563         if(this.jsonData){
25564             var data = [];
25565             var ss = this.snapshot;
25566             if(typeof value == "string"){
25567                 var vlen = value.length;
25568                 if(vlen == 0){
25569                     this.clearFilter();
25570                     return;
25571                 }
25572                 value = value.toLowerCase();
25573                 for(var i = 0, len = ss.length; i < len; i++){
25574                     var o = ss[i];
25575                     if(o[property].substr(0, vlen).toLowerCase() == value){
25576                         data.push(o);
25577                     }
25578                 }
25579             } else if(value.exec){ // regex?
25580                 for(var i = 0, len = ss.length; i < len; i++){
25581                     var o = ss[i];
25582                     if(value.test(o[property])){
25583                         data.push(o);
25584                     }
25585                 }
25586             } else{
25587                 return;
25588             }
25589             this.jsonData = data;
25590             this.refresh();
25591         }
25592     },
25593
25594 /**
25595  * Filter by a function. The passed function will be called with each
25596  * object in the current dataset. If the function returns true the value is kept,
25597  * otherwise it is filtered.
25598  * @param {Function} fn
25599  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25600  */
25601     filterBy : function(fn, scope){
25602         if(this.jsonData){
25603             var data = [];
25604             var ss = this.snapshot;
25605             for(var i = 0, len = ss.length; i < len; i++){
25606                 var o = ss[i];
25607                 if(fn.call(scope || this, o)){
25608                     data.push(o);
25609                 }
25610             }
25611             this.jsonData = data;
25612             this.refresh();
25613         }
25614     },
25615
25616 /**
25617  * Clears the current filter.
25618  */
25619     clearFilter : function(){
25620         if(this.snapshot && this.jsonData != this.snapshot){
25621             this.jsonData = this.snapshot;
25622             this.refresh();
25623         }
25624     },
25625
25626
25627 /**
25628  * Sorts the data for this view and refreshes it.
25629  * @param {String} property A property on your JSON objects to sort on
25630  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25631  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25632  */
25633     sort : function(property, dir, sortType){
25634         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25635         if(this.jsonData){
25636             var p = property;
25637             var dsc = dir && dir.toLowerCase() == "desc";
25638             var f = function(o1, o2){
25639                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25640                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25641                 ;
25642                 if(v1 < v2){
25643                     return dsc ? +1 : -1;
25644                 } else if(v1 > v2){
25645                     return dsc ? -1 : +1;
25646                 } else{
25647                     return 0;
25648                 }
25649             };
25650             this.jsonData.sort(f);
25651             this.refresh();
25652             if(this.jsonData != this.snapshot){
25653                 this.snapshot.sort(f);
25654             }
25655         }
25656     }
25657 });/*
25658  * Based on:
25659  * Ext JS Library 1.1.1
25660  * Copyright(c) 2006-2007, Ext JS, LLC.
25661  *
25662  * Originally Released Under LGPL - original licence link has changed is not relivant.
25663  *
25664  * Fork - LGPL
25665  * <script type="text/javascript">
25666  */
25667  
25668
25669 /**
25670  * @class Roo.ColorPalette
25671  * @extends Roo.Component
25672  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25673  * Here's an example of typical usage:
25674  * <pre><code>
25675 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25676 cp.render('my-div');
25677
25678 cp.on('select', function(palette, selColor){
25679     // do something with selColor
25680 });
25681 </code></pre>
25682  * @constructor
25683  * Create a new ColorPalette
25684  * @param {Object} config The config object
25685  */
25686 Roo.ColorPalette = function(config){
25687     Roo.ColorPalette.superclass.constructor.call(this, config);
25688     this.addEvents({
25689         /**
25690              * @event select
25691              * Fires when a color is selected
25692              * @param {ColorPalette} this
25693              * @param {String} color The 6-digit color hex code (without the # symbol)
25694              */
25695         select: true
25696     });
25697
25698     if(this.handler){
25699         this.on("select", this.handler, this.scope, true);
25700     }
25701 };
25702 Roo.extend(Roo.ColorPalette, Roo.Component, {
25703     /**
25704      * @cfg {String} itemCls
25705      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25706      */
25707     itemCls : "x-color-palette",
25708     /**
25709      * @cfg {String} value
25710      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25711      * the hex codes are case-sensitive.
25712      */
25713     value : null,
25714     clickEvent:'click',
25715     // private
25716     ctype: "Roo.ColorPalette",
25717
25718     /**
25719      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25720      */
25721     allowReselect : false,
25722
25723     /**
25724      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25725      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25726      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25727      * of colors with the width setting until the box is symmetrical.</p>
25728      * <p>You can override individual colors if needed:</p>
25729      * <pre><code>
25730 var cp = new Roo.ColorPalette();
25731 cp.colors[0] = "FF0000";  // change the first box to red
25732 </code></pre>
25733
25734 Or you can provide a custom array of your own for complete control:
25735 <pre><code>
25736 var cp = new Roo.ColorPalette();
25737 cp.colors = ["000000", "993300", "333300"];
25738 </code></pre>
25739      * @type Array
25740      */
25741     colors : [
25742         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25743         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25744         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25745         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25746         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25747     ],
25748
25749     // private
25750     onRender : function(container, position){
25751         var t = new Roo.MasterTemplate(
25752             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25753         );
25754         var c = this.colors;
25755         for(var i = 0, len = c.length; i < len; i++){
25756             t.add([c[i]]);
25757         }
25758         var el = document.createElement("div");
25759         el.className = this.itemCls;
25760         t.overwrite(el);
25761         container.dom.insertBefore(el, position);
25762         this.el = Roo.get(el);
25763         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25764         if(this.clickEvent != 'click'){
25765             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25766         }
25767     },
25768
25769     // private
25770     afterRender : function(){
25771         Roo.ColorPalette.superclass.afterRender.call(this);
25772         if(this.value){
25773             var s = this.value;
25774             this.value = null;
25775             this.select(s);
25776         }
25777     },
25778
25779     // private
25780     handleClick : function(e, t){
25781         e.preventDefault();
25782         if(!this.disabled){
25783             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25784             this.select(c.toUpperCase());
25785         }
25786     },
25787
25788     /**
25789      * Selects the specified color in the palette (fires the select event)
25790      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25791      */
25792     select : function(color){
25793         color = color.replace("#", "");
25794         if(color != this.value || this.allowReselect){
25795             var el = this.el;
25796             if(this.value){
25797                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25798             }
25799             el.child("a.color-"+color).addClass("x-color-palette-sel");
25800             this.value = color;
25801             this.fireEvent("select", this, color);
25802         }
25803     }
25804 });/*
25805  * Based on:
25806  * Ext JS Library 1.1.1
25807  * Copyright(c) 2006-2007, Ext JS, LLC.
25808  *
25809  * Originally Released Under LGPL - original licence link has changed is not relivant.
25810  *
25811  * Fork - LGPL
25812  * <script type="text/javascript">
25813  */
25814  
25815 /**
25816  * @class Roo.DatePicker
25817  * @extends Roo.Component
25818  * Simple date picker class.
25819  * @constructor
25820  * Create a new DatePicker
25821  * @param {Object} config The config object
25822  */
25823 Roo.DatePicker = function(config){
25824     Roo.DatePicker.superclass.constructor.call(this, config);
25825
25826     this.value = config && config.value ?
25827                  config.value.clearTime() : new Date().clearTime();
25828
25829     this.addEvents({
25830         /**
25831              * @event select
25832              * Fires when a date is selected
25833              * @param {DatePicker} this
25834              * @param {Date} date The selected date
25835              */
25836         'select': true,
25837         /**
25838              * @event monthchange
25839              * Fires when the displayed month changes 
25840              * @param {DatePicker} this
25841              * @param {Date} date The selected month
25842              */
25843         'monthchange': true
25844     });
25845
25846     if(this.handler){
25847         this.on("select", this.handler,  this.scope || this);
25848     }
25849     // build the disabledDatesRE
25850     if(!this.disabledDatesRE && this.disabledDates){
25851         var dd = this.disabledDates;
25852         var re = "(?:";
25853         for(var i = 0; i < dd.length; i++){
25854             re += dd[i];
25855             if(i != dd.length-1) re += "|";
25856         }
25857         this.disabledDatesRE = new RegExp(re + ")");
25858     }
25859 };
25860
25861 Roo.extend(Roo.DatePicker, Roo.Component, {
25862     /**
25863      * @cfg {String} todayText
25864      * The text to display on the button that selects the current date (defaults to "Today")
25865      */
25866     todayText : "Today",
25867     /**
25868      * @cfg {String} okText
25869      * The text to display on the ok button
25870      */
25871     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25872     /**
25873      * @cfg {String} cancelText
25874      * The text to display on the cancel button
25875      */
25876     cancelText : "Cancel",
25877     /**
25878      * @cfg {String} todayTip
25879      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25880      */
25881     todayTip : "{0} (Spacebar)",
25882     /**
25883      * @cfg {Date} minDate
25884      * Minimum allowable date (JavaScript date object, defaults to null)
25885      */
25886     minDate : null,
25887     /**
25888      * @cfg {Date} maxDate
25889      * Maximum allowable date (JavaScript date object, defaults to null)
25890      */
25891     maxDate : null,
25892     /**
25893      * @cfg {String} minText
25894      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25895      */
25896     minText : "This date is before the minimum date",
25897     /**
25898      * @cfg {String} maxText
25899      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25900      */
25901     maxText : "This date is after the maximum date",
25902     /**
25903      * @cfg {String} format
25904      * The default date format string which can be overriden for localization support.  The format must be
25905      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25906      */
25907     format : "m/d/y",
25908     /**
25909      * @cfg {Array} disabledDays
25910      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25911      */
25912     disabledDays : null,
25913     /**
25914      * @cfg {String} disabledDaysText
25915      * The tooltip to display when the date falls on a disabled day (defaults to "")
25916      */
25917     disabledDaysText : "",
25918     /**
25919      * @cfg {RegExp} disabledDatesRE
25920      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25921      */
25922     disabledDatesRE : null,
25923     /**
25924      * @cfg {String} disabledDatesText
25925      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25926      */
25927     disabledDatesText : "",
25928     /**
25929      * @cfg {Boolean} constrainToViewport
25930      * True to constrain the date picker to the viewport (defaults to true)
25931      */
25932     constrainToViewport : true,
25933     /**
25934      * @cfg {Array} monthNames
25935      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25936      */
25937     monthNames : Date.monthNames,
25938     /**
25939      * @cfg {Array} dayNames
25940      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25941      */
25942     dayNames : Date.dayNames,
25943     /**
25944      * @cfg {String} nextText
25945      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25946      */
25947     nextText: 'Next Month (Control+Right)',
25948     /**
25949      * @cfg {String} prevText
25950      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25951      */
25952     prevText: 'Previous Month (Control+Left)',
25953     /**
25954      * @cfg {String} monthYearText
25955      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25956      */
25957     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25958     /**
25959      * @cfg {Number} startDay
25960      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25961      */
25962     startDay : 0,
25963     /**
25964      * @cfg {Bool} showClear
25965      * Show a clear button (usefull for date form elements that can be blank.)
25966      */
25967     
25968     showClear: false,
25969     
25970     /**
25971      * Sets the value of the date field
25972      * @param {Date} value The date to set
25973      */
25974     setValue : function(value){
25975         var old = this.value;
25976         
25977         if (typeof(value) == 'string') {
25978          
25979             value = Date.parseDate(value, this.format);
25980         }
25981         if (!value) {
25982             value = new Date();
25983         }
25984         
25985         this.value = value.clearTime(true);
25986         if(this.el){
25987             this.update(this.value);
25988         }
25989     },
25990
25991     /**
25992      * Gets the current selected value of the date field
25993      * @return {Date} The selected date
25994      */
25995     getValue : function(){
25996         return this.value;
25997     },
25998
25999     // private
26000     focus : function(){
26001         if(this.el){
26002             this.update(this.activeDate);
26003         }
26004     },
26005
26006     // privateval
26007     onRender : function(container, position){
26008         
26009         var m = [
26010              '<table cellspacing="0">',
26011                 '<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>',
26012                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26013         var dn = this.dayNames;
26014         for(var i = 0; i < 7; i++){
26015             var d = this.startDay+i;
26016             if(d > 6){
26017                 d = d-7;
26018             }
26019             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26020         }
26021         m[m.length] = "</tr></thead><tbody><tr>";
26022         for(var i = 0; i < 42; i++) {
26023             if(i % 7 == 0 && i != 0){
26024                 m[m.length] = "</tr><tr>";
26025             }
26026             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26027         }
26028         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26029             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26030
26031         var el = document.createElement("div");
26032         el.className = "x-date-picker";
26033         el.innerHTML = m.join("");
26034
26035         container.dom.insertBefore(el, position);
26036
26037         this.el = Roo.get(el);
26038         this.eventEl = Roo.get(el.firstChild);
26039
26040         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26041             handler: this.showPrevMonth,
26042             scope: this,
26043             preventDefault:true,
26044             stopDefault:true
26045         });
26046
26047         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26048             handler: this.showNextMonth,
26049             scope: this,
26050             preventDefault:true,
26051             stopDefault:true
26052         });
26053
26054         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26055
26056         this.monthPicker = this.el.down('div.x-date-mp');
26057         this.monthPicker.enableDisplayMode('block');
26058         
26059         var kn = new Roo.KeyNav(this.eventEl, {
26060             "left" : function(e){
26061                 e.ctrlKey ?
26062                     this.showPrevMonth() :
26063                     this.update(this.activeDate.add("d", -1));
26064             },
26065
26066             "right" : function(e){
26067                 e.ctrlKey ?
26068                     this.showNextMonth() :
26069                     this.update(this.activeDate.add("d", 1));
26070             },
26071
26072             "up" : function(e){
26073                 e.ctrlKey ?
26074                     this.showNextYear() :
26075                     this.update(this.activeDate.add("d", -7));
26076             },
26077
26078             "down" : function(e){
26079                 e.ctrlKey ?
26080                     this.showPrevYear() :
26081                     this.update(this.activeDate.add("d", 7));
26082             },
26083
26084             "pageUp" : function(e){
26085                 this.showNextMonth();
26086             },
26087
26088             "pageDown" : function(e){
26089                 this.showPrevMonth();
26090             },
26091
26092             "enter" : function(e){
26093                 e.stopPropagation();
26094                 return true;
26095             },
26096
26097             scope : this
26098         });
26099
26100         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26101
26102         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26103
26104         this.el.unselectable();
26105         
26106         this.cells = this.el.select("table.x-date-inner tbody td");
26107         this.textNodes = this.el.query("table.x-date-inner tbody span");
26108
26109         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26110             text: "&#160;",
26111             tooltip: this.monthYearText
26112         });
26113
26114         this.mbtn.on('click', this.showMonthPicker, this);
26115         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26116
26117
26118         var today = (new Date()).dateFormat(this.format);
26119         
26120         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26121         if (this.showClear) {
26122             baseTb.add( new Roo.Toolbar.Fill());
26123         }
26124         baseTb.add({
26125             text: String.format(this.todayText, today),
26126             tooltip: String.format(this.todayTip, today),
26127             handler: this.selectToday,
26128             scope: this
26129         });
26130         
26131         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26132             
26133         //});
26134         if (this.showClear) {
26135             
26136             baseTb.add( new Roo.Toolbar.Fill());
26137             baseTb.add({
26138                 text: '&#160;',
26139                 cls: 'x-btn-icon x-btn-clear',
26140                 handler: function() {
26141                     //this.value = '';
26142                     this.fireEvent("select", this, '');
26143                 },
26144                 scope: this
26145             });
26146         }
26147         
26148         
26149         if(Roo.isIE){
26150             this.el.repaint();
26151         }
26152         this.update(this.value);
26153     },
26154
26155     createMonthPicker : function(){
26156         if(!this.monthPicker.dom.firstChild){
26157             var buf = ['<table border="0" cellspacing="0">'];
26158             for(var i = 0; i < 6; i++){
26159                 buf.push(
26160                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26161                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26162                     i == 0 ?
26163                     '<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>' :
26164                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26165                 );
26166             }
26167             buf.push(
26168                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26169                     this.okText,
26170                     '</button><button type="button" class="x-date-mp-cancel">',
26171                     this.cancelText,
26172                     '</button></td></tr>',
26173                 '</table>'
26174             );
26175             this.monthPicker.update(buf.join(''));
26176             this.monthPicker.on('click', this.onMonthClick, this);
26177             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26178
26179             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26180             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26181
26182             this.mpMonths.each(function(m, a, i){
26183                 i += 1;
26184                 if((i%2) == 0){
26185                     m.dom.xmonth = 5 + Math.round(i * .5);
26186                 }else{
26187                     m.dom.xmonth = Math.round((i-1) * .5);
26188                 }
26189             });
26190         }
26191     },
26192
26193     showMonthPicker : function(){
26194         this.createMonthPicker();
26195         var size = this.el.getSize();
26196         this.monthPicker.setSize(size);
26197         this.monthPicker.child('table').setSize(size);
26198
26199         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26200         this.updateMPMonth(this.mpSelMonth);
26201         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26202         this.updateMPYear(this.mpSelYear);
26203
26204         this.monthPicker.slideIn('t', {duration:.2});
26205     },
26206
26207     updateMPYear : function(y){
26208         this.mpyear = y;
26209         var ys = this.mpYears.elements;
26210         for(var i = 1; i <= 10; i++){
26211             var td = ys[i-1], y2;
26212             if((i%2) == 0){
26213                 y2 = y + Math.round(i * .5);
26214                 td.firstChild.innerHTML = y2;
26215                 td.xyear = y2;
26216             }else{
26217                 y2 = y - (5-Math.round(i * .5));
26218                 td.firstChild.innerHTML = y2;
26219                 td.xyear = y2;
26220             }
26221             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26222         }
26223     },
26224
26225     updateMPMonth : function(sm){
26226         this.mpMonths.each(function(m, a, i){
26227             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26228         });
26229     },
26230
26231     selectMPMonth: function(m){
26232         
26233     },
26234
26235     onMonthClick : function(e, t){
26236         e.stopEvent();
26237         var el = new Roo.Element(t), pn;
26238         if(el.is('button.x-date-mp-cancel')){
26239             this.hideMonthPicker();
26240         }
26241         else if(el.is('button.x-date-mp-ok')){
26242             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26243             this.hideMonthPicker();
26244         }
26245         else if(pn = el.up('td.x-date-mp-month', 2)){
26246             this.mpMonths.removeClass('x-date-mp-sel');
26247             pn.addClass('x-date-mp-sel');
26248             this.mpSelMonth = pn.dom.xmonth;
26249         }
26250         else if(pn = el.up('td.x-date-mp-year', 2)){
26251             this.mpYears.removeClass('x-date-mp-sel');
26252             pn.addClass('x-date-mp-sel');
26253             this.mpSelYear = pn.dom.xyear;
26254         }
26255         else if(el.is('a.x-date-mp-prev')){
26256             this.updateMPYear(this.mpyear-10);
26257         }
26258         else if(el.is('a.x-date-mp-next')){
26259             this.updateMPYear(this.mpyear+10);
26260         }
26261     },
26262
26263     onMonthDblClick : function(e, t){
26264         e.stopEvent();
26265         var el = new Roo.Element(t), pn;
26266         if(pn = el.up('td.x-date-mp-month', 2)){
26267             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26268             this.hideMonthPicker();
26269         }
26270         else if(pn = el.up('td.x-date-mp-year', 2)){
26271             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26272             this.hideMonthPicker();
26273         }
26274     },
26275
26276     hideMonthPicker : function(disableAnim){
26277         if(this.monthPicker){
26278             if(disableAnim === true){
26279                 this.monthPicker.hide();
26280             }else{
26281                 this.monthPicker.slideOut('t', {duration:.2});
26282             }
26283         }
26284     },
26285
26286     // private
26287     showPrevMonth : function(e){
26288         this.update(this.activeDate.add("mo", -1));
26289     },
26290
26291     // private
26292     showNextMonth : function(e){
26293         this.update(this.activeDate.add("mo", 1));
26294     },
26295
26296     // private
26297     showPrevYear : function(){
26298         this.update(this.activeDate.add("y", -1));
26299     },
26300
26301     // private
26302     showNextYear : function(){
26303         this.update(this.activeDate.add("y", 1));
26304     },
26305
26306     // private
26307     handleMouseWheel : function(e){
26308         var delta = e.getWheelDelta();
26309         if(delta > 0){
26310             this.showPrevMonth();
26311             e.stopEvent();
26312         } else if(delta < 0){
26313             this.showNextMonth();
26314             e.stopEvent();
26315         }
26316     },
26317
26318     // private
26319     handleDateClick : function(e, t){
26320         e.stopEvent();
26321         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26322             this.setValue(new Date(t.dateValue));
26323             this.fireEvent("select", this, this.value);
26324         }
26325     },
26326
26327     // private
26328     selectToday : function(){
26329         this.setValue(new Date().clearTime());
26330         this.fireEvent("select", this, this.value);
26331     },
26332
26333     // private
26334     update : function(date)
26335     {
26336         var vd = this.activeDate;
26337         this.activeDate = date;
26338         if(vd && this.el){
26339             var t = date.getTime();
26340             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26341                 this.cells.removeClass("x-date-selected");
26342                 this.cells.each(function(c){
26343                    if(c.dom.firstChild.dateValue == t){
26344                        c.addClass("x-date-selected");
26345                        setTimeout(function(){
26346                             try{c.dom.firstChild.focus();}catch(e){}
26347                        }, 50);
26348                        return false;
26349                    }
26350                 });
26351                 return;
26352             }
26353         }
26354         
26355         var days = date.getDaysInMonth();
26356         var firstOfMonth = date.getFirstDateOfMonth();
26357         var startingPos = firstOfMonth.getDay()-this.startDay;
26358
26359         if(startingPos <= this.startDay){
26360             startingPos += 7;
26361         }
26362
26363         var pm = date.add("mo", -1);
26364         var prevStart = pm.getDaysInMonth()-startingPos;
26365
26366         var cells = this.cells.elements;
26367         var textEls = this.textNodes;
26368         days += startingPos;
26369
26370         // convert everything to numbers so it's fast
26371         var day = 86400000;
26372         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26373         var today = new Date().clearTime().getTime();
26374         var sel = date.clearTime().getTime();
26375         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26376         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26377         var ddMatch = this.disabledDatesRE;
26378         var ddText = this.disabledDatesText;
26379         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26380         var ddaysText = this.disabledDaysText;
26381         var format = this.format;
26382
26383         var setCellClass = function(cal, cell){
26384             cell.title = "";
26385             var t = d.getTime();
26386             cell.firstChild.dateValue = t;
26387             if(t == today){
26388                 cell.className += " x-date-today";
26389                 cell.title = cal.todayText;
26390             }
26391             if(t == sel){
26392                 cell.className += " x-date-selected";
26393                 setTimeout(function(){
26394                     try{cell.firstChild.focus();}catch(e){}
26395                 }, 50);
26396             }
26397             // disabling
26398             if(t < min) {
26399                 cell.className = " x-date-disabled";
26400                 cell.title = cal.minText;
26401                 return;
26402             }
26403             if(t > max) {
26404                 cell.className = " x-date-disabled";
26405                 cell.title = cal.maxText;
26406                 return;
26407             }
26408             if(ddays){
26409                 if(ddays.indexOf(d.getDay()) != -1){
26410                     cell.title = ddaysText;
26411                     cell.className = " x-date-disabled";
26412                 }
26413             }
26414             if(ddMatch && format){
26415                 var fvalue = d.dateFormat(format);
26416                 if(ddMatch.test(fvalue)){
26417                     cell.title = ddText.replace("%0", fvalue);
26418                     cell.className = " x-date-disabled";
26419                 }
26420             }
26421         };
26422
26423         var i = 0;
26424         for(; i < startingPos; i++) {
26425             textEls[i].innerHTML = (++prevStart);
26426             d.setDate(d.getDate()+1);
26427             cells[i].className = "x-date-prevday";
26428             setCellClass(this, cells[i]);
26429         }
26430         for(; i < days; i++){
26431             intDay = i - startingPos + 1;
26432             textEls[i].innerHTML = (intDay);
26433             d.setDate(d.getDate()+1);
26434             cells[i].className = "x-date-active";
26435             setCellClass(this, cells[i]);
26436         }
26437         var extraDays = 0;
26438         for(; i < 42; i++) {
26439              textEls[i].innerHTML = (++extraDays);
26440              d.setDate(d.getDate()+1);
26441              cells[i].className = "x-date-nextday";
26442              setCellClass(this, cells[i]);
26443         }
26444
26445         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26446         this.fireEvent('monthchange', this, date);
26447         
26448         if(!this.internalRender){
26449             var main = this.el.dom.firstChild;
26450             var w = main.offsetWidth;
26451             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26452             Roo.fly(main).setWidth(w);
26453             this.internalRender = true;
26454             // opera does not respect the auto grow header center column
26455             // then, after it gets a width opera refuses to recalculate
26456             // without a second pass
26457             if(Roo.isOpera && !this.secondPass){
26458                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26459                 this.secondPass = true;
26460                 this.update.defer(10, this, [date]);
26461             }
26462         }
26463         
26464         
26465     }
26466 });        /*
26467  * Based on:
26468  * Ext JS Library 1.1.1
26469  * Copyright(c) 2006-2007, Ext JS, LLC.
26470  *
26471  * Originally Released Under LGPL - original licence link has changed is not relivant.
26472  *
26473  * Fork - LGPL
26474  * <script type="text/javascript">
26475  */
26476 /**
26477  * @class Roo.TabPanel
26478  * @extends Roo.util.Observable
26479  * A lightweight tab container.
26480  * <br><br>
26481  * Usage:
26482  * <pre><code>
26483 // basic tabs 1, built from existing content
26484 var tabs = new Roo.TabPanel("tabs1");
26485 tabs.addTab("script", "View Script");
26486 tabs.addTab("markup", "View Markup");
26487 tabs.activate("script");
26488
26489 // more advanced tabs, built from javascript
26490 var jtabs = new Roo.TabPanel("jtabs");
26491 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26492
26493 // set up the UpdateManager
26494 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26495 var updater = tab2.getUpdateManager();
26496 updater.setDefaultUrl("ajax1.htm");
26497 tab2.on('activate', updater.refresh, updater, true);
26498
26499 // Use setUrl for Ajax loading
26500 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26501 tab3.setUrl("ajax2.htm", null, true);
26502
26503 // Disabled tab
26504 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26505 tab4.disable();
26506
26507 jtabs.activate("jtabs-1");
26508  * </code></pre>
26509  * @constructor
26510  * Create a new TabPanel.
26511  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26512  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26513  */
26514 Roo.TabPanel = function(container, config){
26515     /**
26516     * The container element for this TabPanel.
26517     * @type Roo.Element
26518     */
26519     this.el = Roo.get(container, true);
26520     if(config){
26521         if(typeof config == "boolean"){
26522             this.tabPosition = config ? "bottom" : "top";
26523         }else{
26524             Roo.apply(this, config);
26525         }
26526     }
26527     if(this.tabPosition == "bottom"){
26528         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26529         this.el.addClass("x-tabs-bottom");
26530     }
26531     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26532     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26533     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26534     if(Roo.isIE){
26535         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26536     }
26537     if(this.tabPosition != "bottom"){
26538         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26539          * @type Roo.Element
26540          */
26541         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26542         this.el.addClass("x-tabs-top");
26543     }
26544     this.items = [];
26545
26546     this.bodyEl.setStyle("position", "relative");
26547
26548     this.active = null;
26549     this.activateDelegate = this.activate.createDelegate(this);
26550
26551     this.addEvents({
26552         /**
26553          * @event tabchange
26554          * Fires when the active tab changes
26555          * @param {Roo.TabPanel} this
26556          * @param {Roo.TabPanelItem} activePanel The new active tab
26557          */
26558         "tabchange": true,
26559         /**
26560          * @event beforetabchange
26561          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26562          * @param {Roo.TabPanel} this
26563          * @param {Object} e Set cancel to true on this object to cancel the tab change
26564          * @param {Roo.TabPanelItem} tab The tab being changed to
26565          */
26566         "beforetabchange" : true
26567     });
26568
26569     Roo.EventManager.onWindowResize(this.onResize, this);
26570     this.cpad = this.el.getPadding("lr");
26571     this.hiddenCount = 0;
26572
26573
26574     // toolbar on the tabbar support...
26575     if (this.toolbar) {
26576         var tcfg = this.toolbar;
26577         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26578         this.toolbar = new Roo.Toolbar(tcfg);
26579         if (Roo.isSafari) {
26580             var tbl = tcfg.container.child('table', true);
26581             tbl.setAttribute('width', '100%');
26582         }
26583         
26584     }
26585    
26586
26587
26588     Roo.TabPanel.superclass.constructor.call(this);
26589 };
26590
26591 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26592     /*
26593      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26594      */
26595     tabPosition : "top",
26596     /*
26597      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26598      */
26599     currentTabWidth : 0,
26600     /*
26601      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26602      */
26603     minTabWidth : 40,
26604     /*
26605      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26606      */
26607     maxTabWidth : 250,
26608     /*
26609      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26610      */
26611     preferredTabWidth : 175,
26612     /*
26613      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26614      */
26615     resizeTabs : false,
26616     /*
26617      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26618      */
26619     monitorResize : true,
26620     /*
26621      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26622      */
26623     toolbar : false,
26624
26625     /**
26626      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26627      * @param {String} id The id of the div to use <b>or create</b>
26628      * @param {String} text The text for the tab
26629      * @param {String} content (optional) Content to put in the TabPanelItem body
26630      * @param {Boolean} closable (optional) True to create a close icon on the tab
26631      * @return {Roo.TabPanelItem} The created TabPanelItem
26632      */
26633     addTab : function(id, text, content, closable){
26634         var item = new Roo.TabPanelItem(this, id, text, closable);
26635         this.addTabItem(item);
26636         if(content){
26637             item.setContent(content);
26638         }
26639         return item;
26640     },
26641
26642     /**
26643      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26644      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26645      * @return {Roo.TabPanelItem}
26646      */
26647     getTab : function(id){
26648         return this.items[id];
26649     },
26650
26651     /**
26652      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26653      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26654      */
26655     hideTab : function(id){
26656         var t = this.items[id];
26657         if(!t.isHidden()){
26658            t.setHidden(true);
26659            this.hiddenCount++;
26660            this.autoSizeTabs();
26661         }
26662     },
26663
26664     /**
26665      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26666      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26667      */
26668     unhideTab : function(id){
26669         var t = this.items[id];
26670         if(t.isHidden()){
26671            t.setHidden(false);
26672            this.hiddenCount--;
26673            this.autoSizeTabs();
26674         }
26675     },
26676
26677     /**
26678      * Adds an existing {@link Roo.TabPanelItem}.
26679      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26680      */
26681     addTabItem : function(item){
26682         this.items[item.id] = item;
26683         this.items.push(item);
26684         if(this.resizeTabs){
26685            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26686            this.autoSizeTabs();
26687         }else{
26688             item.autoSize();
26689         }
26690     },
26691
26692     /**
26693      * Removes a {@link Roo.TabPanelItem}.
26694      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26695      */
26696     removeTab : function(id){
26697         var items = this.items;
26698         var tab = items[id];
26699         if(!tab) { return; }
26700         var index = items.indexOf(tab);
26701         if(this.active == tab && items.length > 1){
26702             var newTab = this.getNextAvailable(index);
26703             if(newTab) {
26704                 newTab.activate();
26705             }
26706         }
26707         this.stripEl.dom.removeChild(tab.pnode.dom);
26708         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26709             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26710         }
26711         items.splice(index, 1);
26712         delete this.items[tab.id];
26713         tab.fireEvent("close", tab);
26714         tab.purgeListeners();
26715         this.autoSizeTabs();
26716     },
26717
26718     getNextAvailable : function(start){
26719         var items = this.items;
26720         var index = start;
26721         // look for a next tab that will slide over to
26722         // replace the one being removed
26723         while(index < items.length){
26724             var item = items[++index];
26725             if(item && !item.isHidden()){
26726                 return item;
26727             }
26728         }
26729         // if one isn't found select the previous tab (on the left)
26730         index = start;
26731         while(index >= 0){
26732             var item = items[--index];
26733             if(item && !item.isHidden()){
26734                 return item;
26735             }
26736         }
26737         return null;
26738     },
26739
26740     /**
26741      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26742      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26743      */
26744     disableTab : function(id){
26745         var tab = this.items[id];
26746         if(tab && this.active != tab){
26747             tab.disable();
26748         }
26749     },
26750
26751     /**
26752      * Enables a {@link Roo.TabPanelItem} that is disabled.
26753      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26754      */
26755     enableTab : function(id){
26756         var tab = this.items[id];
26757         tab.enable();
26758     },
26759
26760     /**
26761      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26762      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26763      * @return {Roo.TabPanelItem} The TabPanelItem.
26764      */
26765     activate : function(id){
26766         var tab = this.items[id];
26767         if(!tab){
26768             return null;
26769         }
26770         if(tab == this.active || tab.disabled){
26771             return tab;
26772         }
26773         var e = {};
26774         this.fireEvent("beforetabchange", this, e, tab);
26775         if(e.cancel !== true && !tab.disabled){
26776             if(this.active){
26777                 this.active.hide();
26778             }
26779             this.active = this.items[id];
26780             this.active.show();
26781             this.fireEvent("tabchange", this, this.active);
26782         }
26783         return tab;
26784     },
26785
26786     /**
26787      * Gets the active {@link Roo.TabPanelItem}.
26788      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26789      */
26790     getActiveTab : function(){
26791         return this.active;
26792     },
26793
26794     /**
26795      * Updates the tab body element to fit the height of the container element
26796      * for overflow scrolling
26797      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26798      */
26799     syncHeight : function(targetHeight){
26800         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26801         var bm = this.bodyEl.getMargins();
26802         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26803         this.bodyEl.setHeight(newHeight);
26804         return newHeight;
26805     },
26806
26807     onResize : function(){
26808         if(this.monitorResize){
26809             this.autoSizeTabs();
26810         }
26811     },
26812
26813     /**
26814      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26815      */
26816     beginUpdate : function(){
26817         this.updating = true;
26818     },
26819
26820     /**
26821      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26822      */
26823     endUpdate : function(){
26824         this.updating = false;
26825         this.autoSizeTabs();
26826     },
26827
26828     /**
26829      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26830      */
26831     autoSizeTabs : function(){
26832         var count = this.items.length;
26833         var vcount = count - this.hiddenCount;
26834         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26835         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26836         var availWidth = Math.floor(w / vcount);
26837         var b = this.stripBody;
26838         if(b.getWidth() > w){
26839             var tabs = this.items;
26840             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26841             if(availWidth < this.minTabWidth){
26842                 /*if(!this.sleft){    // incomplete scrolling code
26843                     this.createScrollButtons();
26844                 }
26845                 this.showScroll();
26846                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26847             }
26848         }else{
26849             if(this.currentTabWidth < this.preferredTabWidth){
26850                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26851             }
26852         }
26853     },
26854
26855     /**
26856      * Returns the number of tabs in this TabPanel.
26857      * @return {Number}
26858      */
26859      getCount : function(){
26860          return this.items.length;
26861      },
26862
26863     /**
26864      * Resizes all the tabs to the passed width
26865      * @param {Number} The new width
26866      */
26867     setTabWidth : function(width){
26868         this.currentTabWidth = width;
26869         for(var i = 0, len = this.items.length; i < len; i++) {
26870                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26871         }
26872     },
26873
26874     /**
26875      * Destroys this TabPanel
26876      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26877      */
26878     destroy : function(removeEl){
26879         Roo.EventManager.removeResizeListener(this.onResize, this);
26880         for(var i = 0, len = this.items.length; i < len; i++){
26881             this.items[i].purgeListeners();
26882         }
26883         if(removeEl === true){
26884             this.el.update("");
26885             this.el.remove();
26886         }
26887     }
26888 });
26889
26890 /**
26891  * @class Roo.TabPanelItem
26892  * @extends Roo.util.Observable
26893  * Represents an individual item (tab plus body) in a TabPanel.
26894  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26895  * @param {String} id The id of this TabPanelItem
26896  * @param {String} text The text for the tab of this TabPanelItem
26897  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26898  */
26899 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26900     /**
26901      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26902      * @type Roo.TabPanel
26903      */
26904     this.tabPanel = tabPanel;
26905     /**
26906      * The id for this TabPanelItem
26907      * @type String
26908      */
26909     this.id = id;
26910     /** @private */
26911     this.disabled = false;
26912     /** @private */
26913     this.text = text;
26914     /** @private */
26915     this.loaded = false;
26916     this.closable = closable;
26917
26918     /**
26919      * The body element for this TabPanelItem.
26920      * @type Roo.Element
26921      */
26922     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26923     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26924     this.bodyEl.setStyle("display", "block");
26925     this.bodyEl.setStyle("zoom", "1");
26926     this.hideAction();
26927
26928     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26929     /** @private */
26930     this.el = Roo.get(els.el, true);
26931     this.inner = Roo.get(els.inner, true);
26932     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26933     this.pnode = Roo.get(els.el.parentNode, true);
26934     this.el.on("mousedown", this.onTabMouseDown, this);
26935     this.el.on("click", this.onTabClick, this);
26936     /** @private */
26937     if(closable){
26938         var c = Roo.get(els.close, true);
26939         c.dom.title = this.closeText;
26940         c.addClassOnOver("close-over");
26941         c.on("click", this.closeClick, this);
26942      }
26943
26944     this.addEvents({
26945          /**
26946          * @event activate
26947          * Fires when this tab becomes the active tab.
26948          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26949          * @param {Roo.TabPanelItem} this
26950          */
26951         "activate": true,
26952         /**
26953          * @event beforeclose
26954          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26955          * @param {Roo.TabPanelItem} this
26956          * @param {Object} e Set cancel to true on this object to cancel the close.
26957          */
26958         "beforeclose": true,
26959         /**
26960          * @event close
26961          * Fires when this tab is closed.
26962          * @param {Roo.TabPanelItem} this
26963          */
26964          "close": true,
26965         /**
26966          * @event deactivate
26967          * Fires when this tab is no longer the active tab.
26968          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26969          * @param {Roo.TabPanelItem} this
26970          */
26971          "deactivate" : true
26972     });
26973     this.hidden = false;
26974
26975     Roo.TabPanelItem.superclass.constructor.call(this);
26976 };
26977
26978 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26979     purgeListeners : function(){
26980        Roo.util.Observable.prototype.purgeListeners.call(this);
26981        this.el.removeAllListeners();
26982     },
26983     /**
26984      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26985      */
26986     show : function(){
26987         this.pnode.addClass("on");
26988         this.showAction();
26989         if(Roo.isOpera){
26990             this.tabPanel.stripWrap.repaint();
26991         }
26992         this.fireEvent("activate", this.tabPanel, this);
26993     },
26994
26995     /**
26996      * Returns true if this tab is the active tab.
26997      * @return {Boolean}
26998      */
26999     isActive : function(){
27000         return this.tabPanel.getActiveTab() == this;
27001     },
27002
27003     /**
27004      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27005      */
27006     hide : function(){
27007         this.pnode.removeClass("on");
27008         this.hideAction();
27009         this.fireEvent("deactivate", this.tabPanel, this);
27010     },
27011
27012     hideAction : function(){
27013         this.bodyEl.hide();
27014         this.bodyEl.setStyle("position", "absolute");
27015         this.bodyEl.setLeft("-20000px");
27016         this.bodyEl.setTop("-20000px");
27017     },
27018
27019     showAction : function(){
27020         this.bodyEl.setStyle("position", "relative");
27021         this.bodyEl.setTop("");
27022         this.bodyEl.setLeft("");
27023         this.bodyEl.show();
27024     },
27025
27026     /**
27027      * Set the tooltip for the tab.
27028      * @param {String} tooltip The tab's tooltip
27029      */
27030     setTooltip : function(text){
27031         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27032             this.textEl.dom.qtip = text;
27033             this.textEl.dom.removeAttribute('title');
27034         }else{
27035             this.textEl.dom.title = text;
27036         }
27037     },
27038
27039     onTabClick : function(e){
27040         e.preventDefault();
27041         this.tabPanel.activate(this.id);
27042     },
27043
27044     onTabMouseDown : function(e){
27045         e.preventDefault();
27046         this.tabPanel.activate(this.id);
27047     },
27048
27049     getWidth : function(){
27050         return this.inner.getWidth();
27051     },
27052
27053     setWidth : function(width){
27054         var iwidth = width - this.pnode.getPadding("lr");
27055         this.inner.setWidth(iwidth);
27056         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27057         this.pnode.setWidth(width);
27058     },
27059
27060     /**
27061      * Show or hide the tab
27062      * @param {Boolean} hidden True to hide or false to show.
27063      */
27064     setHidden : function(hidden){
27065         this.hidden = hidden;
27066         this.pnode.setStyle("display", hidden ? "none" : "");
27067     },
27068
27069     /**
27070      * Returns true if this tab is "hidden"
27071      * @return {Boolean}
27072      */
27073     isHidden : function(){
27074         return this.hidden;
27075     },
27076
27077     /**
27078      * Returns the text for this tab
27079      * @return {String}
27080      */
27081     getText : function(){
27082         return this.text;
27083     },
27084
27085     autoSize : function(){
27086         //this.el.beginMeasure();
27087         this.textEl.setWidth(1);
27088         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27089         //this.el.endMeasure();
27090     },
27091
27092     /**
27093      * Sets the text for the tab (Note: this also sets the tooltip text)
27094      * @param {String} text The tab's text and tooltip
27095      */
27096     setText : function(text){
27097         this.text = text;
27098         this.textEl.update(text);
27099         this.setTooltip(text);
27100         if(!this.tabPanel.resizeTabs){
27101             this.autoSize();
27102         }
27103     },
27104     /**
27105      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27106      */
27107     activate : function(){
27108         this.tabPanel.activate(this.id);
27109     },
27110
27111     /**
27112      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27113      */
27114     disable : function(){
27115         if(this.tabPanel.active != this){
27116             this.disabled = true;
27117             this.pnode.addClass("disabled");
27118         }
27119     },
27120
27121     /**
27122      * Enables this TabPanelItem if it was previously disabled.
27123      */
27124     enable : function(){
27125         this.disabled = false;
27126         this.pnode.removeClass("disabled");
27127     },
27128
27129     /**
27130      * Sets the content for this TabPanelItem.
27131      * @param {String} content The content
27132      * @param {Boolean} loadScripts true to look for and load scripts
27133      */
27134     setContent : function(content, loadScripts){
27135         this.bodyEl.update(content, loadScripts);
27136     },
27137
27138     /**
27139      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27140      * @return {Roo.UpdateManager} The UpdateManager
27141      */
27142     getUpdateManager : function(){
27143         return this.bodyEl.getUpdateManager();
27144     },
27145
27146     /**
27147      * Set a URL to be used to load the content for this TabPanelItem.
27148      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27149      * @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)
27150      * @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)
27151      * @return {Roo.UpdateManager} The UpdateManager
27152      */
27153     setUrl : function(url, params, loadOnce){
27154         if(this.refreshDelegate){
27155             this.un('activate', this.refreshDelegate);
27156         }
27157         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27158         this.on("activate", this.refreshDelegate);
27159         return this.bodyEl.getUpdateManager();
27160     },
27161
27162     /** @private */
27163     _handleRefresh : function(url, params, loadOnce){
27164         if(!loadOnce || !this.loaded){
27165             var updater = this.bodyEl.getUpdateManager();
27166             updater.update(url, params, this._setLoaded.createDelegate(this));
27167         }
27168     },
27169
27170     /**
27171      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27172      *   Will fail silently if the setUrl method has not been called.
27173      *   This does not activate the panel, just updates its content.
27174      */
27175     refresh : function(){
27176         if(this.refreshDelegate){
27177            this.loaded = false;
27178            this.refreshDelegate();
27179         }
27180     },
27181
27182     /** @private */
27183     _setLoaded : function(){
27184         this.loaded = true;
27185     },
27186
27187     /** @private */
27188     closeClick : function(e){
27189         var o = {};
27190         e.stopEvent();
27191         this.fireEvent("beforeclose", this, o);
27192         if(o.cancel !== true){
27193             this.tabPanel.removeTab(this.id);
27194         }
27195     },
27196     /**
27197      * The text displayed in the tooltip for the close icon.
27198      * @type String
27199      */
27200     closeText : "Close this tab"
27201 });
27202
27203 /** @private */
27204 Roo.TabPanel.prototype.createStrip = function(container){
27205     var strip = document.createElement("div");
27206     strip.className = "x-tabs-wrap";
27207     container.appendChild(strip);
27208     return strip;
27209 };
27210 /** @private */
27211 Roo.TabPanel.prototype.createStripList = function(strip){
27212     // div wrapper for retard IE
27213     // returns the "tr" element.
27214     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27215         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27216         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27217     return strip.firstChild.firstChild.firstChild.firstChild;
27218 };
27219 /** @private */
27220 Roo.TabPanel.prototype.createBody = function(container){
27221     var body = document.createElement("div");
27222     Roo.id(body, "tab-body");
27223     Roo.fly(body).addClass("x-tabs-body");
27224     container.appendChild(body);
27225     return body;
27226 };
27227 /** @private */
27228 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27229     var body = Roo.getDom(id);
27230     if(!body){
27231         body = document.createElement("div");
27232         body.id = id;
27233     }
27234     Roo.fly(body).addClass("x-tabs-item-body");
27235     bodyEl.insertBefore(body, bodyEl.firstChild);
27236     return body;
27237 };
27238 /** @private */
27239 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27240     var td = document.createElement("td");
27241     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27242     //stripEl.appendChild(td);
27243     if(closable){
27244         td.className = "x-tabs-closable";
27245         if(!this.closeTpl){
27246             this.closeTpl = new Roo.Template(
27247                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27248                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27249                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27250             );
27251         }
27252         var el = this.closeTpl.overwrite(td, {"text": text});
27253         var close = el.getElementsByTagName("div")[0];
27254         var inner = el.getElementsByTagName("em")[0];
27255         return {"el": el, "close": close, "inner": inner};
27256     } else {
27257         if(!this.tabTpl){
27258             this.tabTpl = new Roo.Template(
27259                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27260                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27261             );
27262         }
27263         var el = this.tabTpl.overwrite(td, {"text": text});
27264         var inner = el.getElementsByTagName("em")[0];
27265         return {"el": el, "inner": inner};
27266     }
27267 };/*
27268  * Based on:
27269  * Ext JS Library 1.1.1
27270  * Copyright(c) 2006-2007, Ext JS, LLC.
27271  *
27272  * Originally Released Under LGPL - original licence link has changed is not relivant.
27273  *
27274  * Fork - LGPL
27275  * <script type="text/javascript">
27276  */
27277
27278 /**
27279  * @class Roo.Button
27280  * @extends Roo.util.Observable
27281  * Simple Button class
27282  * @cfg {String} text The button text
27283  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27284  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27285  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27286  * @cfg {Object} scope The scope of the handler
27287  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27288  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27289  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27290  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27291  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27292  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27293    applies if enableToggle = true)
27294  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27295  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27296   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27297  * @constructor
27298  * Create a new button
27299  * @param {Object} config The config object
27300  */
27301 Roo.Button = function(renderTo, config)
27302 {
27303     if (!config) {
27304         config = renderTo;
27305         renderTo = config.renderTo || false;
27306     }
27307     
27308     Roo.apply(this, config);
27309     this.addEvents({
27310         /**
27311              * @event click
27312              * Fires when this button is clicked
27313              * @param {Button} this
27314              * @param {EventObject} e The click event
27315              */
27316             "click" : true,
27317         /**
27318              * @event toggle
27319              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27320              * @param {Button} this
27321              * @param {Boolean} pressed
27322              */
27323             "toggle" : true,
27324         /**
27325              * @event mouseover
27326              * Fires when the mouse hovers over the button
27327              * @param {Button} this
27328              * @param {Event} e The event object
27329              */
27330         'mouseover' : true,
27331         /**
27332              * @event mouseout
27333              * Fires when the mouse exits the button
27334              * @param {Button} this
27335              * @param {Event} e The event object
27336              */
27337         'mouseout': true,
27338          /**
27339              * @event render
27340              * Fires when the button is rendered
27341              * @param {Button} this
27342              */
27343         'render': true
27344     });
27345     if(this.menu){
27346         this.menu = Roo.menu.MenuMgr.get(this.menu);
27347     }
27348     // register listeners first!!  - so render can be captured..
27349     Roo.util.Observable.call(this);
27350     if(renderTo){
27351         this.render(renderTo);
27352     }
27353     
27354   
27355 };
27356
27357 Roo.extend(Roo.Button, Roo.util.Observable, {
27358     /**
27359      * 
27360      */
27361     
27362     /**
27363      * Read-only. True if this button is hidden
27364      * @type Boolean
27365      */
27366     hidden : false,
27367     /**
27368      * Read-only. True if this button is disabled
27369      * @type Boolean
27370      */
27371     disabled : false,
27372     /**
27373      * Read-only. True if this button is pressed (only if enableToggle = true)
27374      * @type Boolean
27375      */
27376     pressed : false,
27377
27378     /**
27379      * @cfg {Number} tabIndex 
27380      * The DOM tabIndex for this button (defaults to undefined)
27381      */
27382     tabIndex : undefined,
27383
27384     /**
27385      * @cfg {Boolean} enableToggle
27386      * True to enable pressed/not pressed toggling (defaults to false)
27387      */
27388     enableToggle: false,
27389     /**
27390      * @cfg {Mixed} menu
27391      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27392      */
27393     menu : undefined,
27394     /**
27395      * @cfg {String} menuAlign
27396      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27397      */
27398     menuAlign : "tl-bl?",
27399
27400     /**
27401      * @cfg {String} iconCls
27402      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27403      */
27404     iconCls : undefined,
27405     /**
27406      * @cfg {String} type
27407      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27408      */
27409     type : 'button',
27410
27411     // private
27412     menuClassTarget: 'tr',
27413
27414     /**
27415      * @cfg {String} clickEvent
27416      * The type of event to map to the button's event handler (defaults to 'click')
27417      */
27418     clickEvent : 'click',
27419
27420     /**
27421      * @cfg {Boolean} handleMouseEvents
27422      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27423      */
27424     handleMouseEvents : true,
27425
27426     /**
27427      * @cfg {String} tooltipType
27428      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27429      */
27430     tooltipType : 'qtip',
27431
27432     /**
27433      * @cfg {String} cls
27434      * A CSS class to apply to the button's main element.
27435      */
27436     
27437     /**
27438      * @cfg {Roo.Template} template (Optional)
27439      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27440      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27441      * require code modifications if required elements (e.g. a button) aren't present.
27442      */
27443
27444     // private
27445     render : function(renderTo){
27446         var btn;
27447         if(this.hideParent){
27448             this.parentEl = Roo.get(renderTo);
27449         }
27450         if(!this.dhconfig){
27451             if(!this.template){
27452                 if(!Roo.Button.buttonTemplate){
27453                     // hideous table template
27454                     Roo.Button.buttonTemplate = new Roo.Template(
27455                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27456                         '<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>',
27457                         "</tr></tbody></table>");
27458                 }
27459                 this.template = Roo.Button.buttonTemplate;
27460             }
27461             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27462             var btnEl = btn.child("button:first");
27463             btnEl.on('focus', this.onFocus, this);
27464             btnEl.on('blur', this.onBlur, this);
27465             if(this.cls){
27466                 btn.addClass(this.cls);
27467             }
27468             if(this.icon){
27469                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27470             }
27471             if(this.iconCls){
27472                 btnEl.addClass(this.iconCls);
27473                 if(!this.cls){
27474                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27475                 }
27476             }
27477             if(this.tabIndex !== undefined){
27478                 btnEl.dom.tabIndex = this.tabIndex;
27479             }
27480             if(this.tooltip){
27481                 if(typeof this.tooltip == 'object'){
27482                     Roo.QuickTips.tips(Roo.apply({
27483                           target: btnEl.id
27484                     }, this.tooltip));
27485                 } else {
27486                     btnEl.dom[this.tooltipType] = this.tooltip;
27487                 }
27488             }
27489         }else{
27490             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27491         }
27492         this.el = btn;
27493         if(this.id){
27494             this.el.dom.id = this.el.id = this.id;
27495         }
27496         if(this.menu){
27497             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27498             this.menu.on("show", this.onMenuShow, this);
27499             this.menu.on("hide", this.onMenuHide, this);
27500         }
27501         btn.addClass("x-btn");
27502         if(Roo.isIE && !Roo.isIE7){
27503             this.autoWidth.defer(1, this);
27504         }else{
27505             this.autoWidth();
27506         }
27507         if(this.handleMouseEvents){
27508             btn.on("mouseover", this.onMouseOver, this);
27509             btn.on("mouseout", this.onMouseOut, this);
27510             btn.on("mousedown", this.onMouseDown, this);
27511         }
27512         btn.on(this.clickEvent, this.onClick, this);
27513         //btn.on("mouseup", this.onMouseUp, this);
27514         if(this.hidden){
27515             this.hide();
27516         }
27517         if(this.disabled){
27518             this.disable();
27519         }
27520         Roo.ButtonToggleMgr.register(this);
27521         if(this.pressed){
27522             this.el.addClass("x-btn-pressed");
27523         }
27524         if(this.repeat){
27525             var repeater = new Roo.util.ClickRepeater(btn,
27526                 typeof this.repeat == "object" ? this.repeat : {}
27527             );
27528             repeater.on("click", this.onClick,  this);
27529         }
27530         
27531         this.fireEvent('render', this);
27532         
27533     },
27534     /**
27535      * Returns the button's underlying element
27536      * @return {Roo.Element} The element
27537      */
27538     getEl : function(){
27539         return this.el;  
27540     },
27541     
27542     /**
27543      * Destroys this Button and removes any listeners.
27544      */
27545     destroy : function(){
27546         Roo.ButtonToggleMgr.unregister(this);
27547         this.el.removeAllListeners();
27548         this.purgeListeners();
27549         this.el.remove();
27550     },
27551
27552     // private
27553     autoWidth : function(){
27554         if(this.el){
27555             this.el.setWidth("auto");
27556             if(Roo.isIE7 && Roo.isStrict){
27557                 var ib = this.el.child('button');
27558                 if(ib && ib.getWidth() > 20){
27559                     ib.clip();
27560                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27561                 }
27562             }
27563             if(this.minWidth){
27564                 if(this.hidden){
27565                     this.el.beginMeasure();
27566                 }
27567                 if(this.el.getWidth() < this.minWidth){
27568                     this.el.setWidth(this.minWidth);
27569                 }
27570                 if(this.hidden){
27571                     this.el.endMeasure();
27572                 }
27573             }
27574         }
27575     },
27576
27577     /**
27578      * Assigns this button's click handler
27579      * @param {Function} handler The function to call when the button is clicked
27580      * @param {Object} scope (optional) Scope for the function passed in
27581      */
27582     setHandler : function(handler, scope){
27583         this.handler = handler;
27584         this.scope = scope;  
27585     },
27586     
27587     /**
27588      * Sets this button's text
27589      * @param {String} text The button text
27590      */
27591     setText : function(text){
27592         this.text = text;
27593         if(this.el){
27594             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27595         }
27596         this.autoWidth();
27597     },
27598     
27599     /**
27600      * Gets the text for this button
27601      * @return {String} The button text
27602      */
27603     getText : function(){
27604         return this.text;  
27605     },
27606     
27607     /**
27608      * Show this button
27609      */
27610     show: function(){
27611         this.hidden = false;
27612         if(this.el){
27613             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27614         }
27615     },
27616     
27617     /**
27618      * Hide this button
27619      */
27620     hide: function(){
27621         this.hidden = true;
27622         if(this.el){
27623             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27624         }
27625     },
27626     
27627     /**
27628      * Convenience function for boolean show/hide
27629      * @param {Boolean} visible True to show, false to hide
27630      */
27631     setVisible: function(visible){
27632         if(visible) {
27633             this.show();
27634         }else{
27635             this.hide();
27636         }
27637     },
27638     
27639     /**
27640      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27641      * @param {Boolean} state (optional) Force a particular state
27642      */
27643     toggle : function(state){
27644         state = state === undefined ? !this.pressed : state;
27645         if(state != this.pressed){
27646             if(state){
27647                 this.el.addClass("x-btn-pressed");
27648                 this.pressed = true;
27649                 this.fireEvent("toggle", this, true);
27650             }else{
27651                 this.el.removeClass("x-btn-pressed");
27652                 this.pressed = false;
27653                 this.fireEvent("toggle", this, false);
27654             }
27655             if(this.toggleHandler){
27656                 this.toggleHandler.call(this.scope || this, this, state);
27657             }
27658         }
27659     },
27660     
27661     /**
27662      * Focus the button
27663      */
27664     focus : function(){
27665         this.el.child('button:first').focus();
27666     },
27667     
27668     /**
27669      * Disable this button
27670      */
27671     disable : function(){
27672         if(this.el){
27673             this.el.addClass("x-btn-disabled");
27674         }
27675         this.disabled = true;
27676     },
27677     
27678     /**
27679      * Enable this button
27680      */
27681     enable : function(){
27682         if(this.el){
27683             this.el.removeClass("x-btn-disabled");
27684         }
27685         this.disabled = false;
27686     },
27687
27688     /**
27689      * Convenience function for boolean enable/disable
27690      * @param {Boolean} enabled True to enable, false to disable
27691      */
27692     setDisabled : function(v){
27693         this[v !== true ? "enable" : "disable"]();
27694     },
27695
27696     // private
27697     onClick : function(e){
27698         if(e){
27699             e.preventDefault();
27700         }
27701         if(e.button != 0){
27702             return;
27703         }
27704         if(!this.disabled){
27705             if(this.enableToggle){
27706                 this.toggle();
27707             }
27708             if(this.menu && !this.menu.isVisible()){
27709                 this.menu.show(this.el, this.menuAlign);
27710             }
27711             this.fireEvent("click", this, e);
27712             if(this.handler){
27713                 this.el.removeClass("x-btn-over");
27714                 this.handler.call(this.scope || this, this, e);
27715             }
27716         }
27717     },
27718     // private
27719     onMouseOver : function(e){
27720         if(!this.disabled){
27721             this.el.addClass("x-btn-over");
27722             this.fireEvent('mouseover', this, e);
27723         }
27724     },
27725     // private
27726     onMouseOut : function(e){
27727         if(!e.within(this.el,  true)){
27728             this.el.removeClass("x-btn-over");
27729             this.fireEvent('mouseout', this, e);
27730         }
27731     },
27732     // private
27733     onFocus : function(e){
27734         if(!this.disabled){
27735             this.el.addClass("x-btn-focus");
27736         }
27737     },
27738     // private
27739     onBlur : function(e){
27740         this.el.removeClass("x-btn-focus");
27741     },
27742     // private
27743     onMouseDown : function(e){
27744         if(!this.disabled && e.button == 0){
27745             this.el.addClass("x-btn-click");
27746             Roo.get(document).on('mouseup', this.onMouseUp, this);
27747         }
27748     },
27749     // private
27750     onMouseUp : function(e){
27751         if(e.button == 0){
27752             this.el.removeClass("x-btn-click");
27753             Roo.get(document).un('mouseup', this.onMouseUp, this);
27754         }
27755     },
27756     // private
27757     onMenuShow : function(e){
27758         this.el.addClass("x-btn-menu-active");
27759     },
27760     // private
27761     onMenuHide : function(e){
27762         this.el.removeClass("x-btn-menu-active");
27763     }   
27764 });
27765
27766 // Private utility class used by Button
27767 Roo.ButtonToggleMgr = function(){
27768    var groups = {};
27769    
27770    function toggleGroup(btn, state){
27771        if(state){
27772            var g = groups[btn.toggleGroup];
27773            for(var i = 0, l = g.length; i < l; i++){
27774                if(g[i] != btn){
27775                    g[i].toggle(false);
27776                }
27777            }
27778        }
27779    }
27780    
27781    return {
27782        register : function(btn){
27783            if(!btn.toggleGroup){
27784                return;
27785            }
27786            var g = groups[btn.toggleGroup];
27787            if(!g){
27788                g = groups[btn.toggleGroup] = [];
27789            }
27790            g.push(btn);
27791            btn.on("toggle", toggleGroup);
27792        },
27793        
27794        unregister : function(btn){
27795            if(!btn.toggleGroup){
27796                return;
27797            }
27798            var g = groups[btn.toggleGroup];
27799            if(g){
27800                g.remove(btn);
27801                btn.un("toggle", toggleGroup);
27802            }
27803        }
27804    };
27805 }();/*
27806  * Based on:
27807  * Ext JS Library 1.1.1
27808  * Copyright(c) 2006-2007, Ext JS, LLC.
27809  *
27810  * Originally Released Under LGPL - original licence link has changed is not relivant.
27811  *
27812  * Fork - LGPL
27813  * <script type="text/javascript">
27814  */
27815  
27816 /**
27817  * @class Roo.SplitButton
27818  * @extends Roo.Button
27819  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27820  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27821  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27822  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27823  * @cfg {String} arrowTooltip The title attribute of the arrow
27824  * @constructor
27825  * Create a new menu button
27826  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27827  * @param {Object} config The config object
27828  */
27829 Roo.SplitButton = function(renderTo, config){
27830     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27831     /**
27832      * @event arrowclick
27833      * Fires when this button's arrow is clicked
27834      * @param {SplitButton} this
27835      * @param {EventObject} e The click event
27836      */
27837     this.addEvents({"arrowclick":true});
27838 };
27839
27840 Roo.extend(Roo.SplitButton, Roo.Button, {
27841     render : function(renderTo){
27842         // this is one sweet looking template!
27843         var tpl = new Roo.Template(
27844             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27845             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27846             '<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>',
27847             "</tbody></table></td><td>",
27848             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27849             '<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>',
27850             "</tbody></table></td></tr></table>"
27851         );
27852         var btn = tpl.append(renderTo, [this.text, this.type], true);
27853         var btnEl = btn.child("button");
27854         if(this.cls){
27855             btn.addClass(this.cls);
27856         }
27857         if(this.icon){
27858             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27859         }
27860         if(this.iconCls){
27861             btnEl.addClass(this.iconCls);
27862             if(!this.cls){
27863                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27864             }
27865         }
27866         this.el = btn;
27867         if(this.handleMouseEvents){
27868             btn.on("mouseover", this.onMouseOver, this);
27869             btn.on("mouseout", this.onMouseOut, this);
27870             btn.on("mousedown", this.onMouseDown, this);
27871             btn.on("mouseup", this.onMouseUp, this);
27872         }
27873         btn.on(this.clickEvent, this.onClick, this);
27874         if(this.tooltip){
27875             if(typeof this.tooltip == 'object'){
27876                 Roo.QuickTips.tips(Roo.apply({
27877                       target: btnEl.id
27878                 }, this.tooltip));
27879             } else {
27880                 btnEl.dom[this.tooltipType] = this.tooltip;
27881             }
27882         }
27883         if(this.arrowTooltip){
27884             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27885         }
27886         if(this.hidden){
27887             this.hide();
27888         }
27889         if(this.disabled){
27890             this.disable();
27891         }
27892         if(this.pressed){
27893             this.el.addClass("x-btn-pressed");
27894         }
27895         if(Roo.isIE && !Roo.isIE7){
27896             this.autoWidth.defer(1, this);
27897         }else{
27898             this.autoWidth();
27899         }
27900         if(this.menu){
27901             this.menu.on("show", this.onMenuShow, this);
27902             this.menu.on("hide", this.onMenuHide, this);
27903         }
27904         this.fireEvent('render', this);
27905     },
27906
27907     // private
27908     autoWidth : function(){
27909         if(this.el){
27910             var tbl = this.el.child("table:first");
27911             var tbl2 = this.el.child("table:last");
27912             this.el.setWidth("auto");
27913             tbl.setWidth("auto");
27914             if(Roo.isIE7 && Roo.isStrict){
27915                 var ib = this.el.child('button:first');
27916                 if(ib && ib.getWidth() > 20){
27917                     ib.clip();
27918                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27919                 }
27920             }
27921             if(this.minWidth){
27922                 if(this.hidden){
27923                     this.el.beginMeasure();
27924                 }
27925                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27926                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27927                 }
27928                 if(this.hidden){
27929                     this.el.endMeasure();
27930                 }
27931             }
27932             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27933         } 
27934     },
27935     /**
27936      * Sets this button's click handler
27937      * @param {Function} handler The function to call when the button is clicked
27938      * @param {Object} scope (optional) Scope for the function passed above
27939      */
27940     setHandler : function(handler, scope){
27941         this.handler = handler;
27942         this.scope = scope;  
27943     },
27944     
27945     /**
27946      * Sets this button's arrow click handler
27947      * @param {Function} handler The function to call when the arrow is clicked
27948      * @param {Object} scope (optional) Scope for the function passed above
27949      */
27950     setArrowHandler : function(handler, scope){
27951         this.arrowHandler = handler;
27952         this.scope = scope;  
27953     },
27954     
27955     /**
27956      * Focus the button
27957      */
27958     focus : function(){
27959         if(this.el){
27960             this.el.child("button:first").focus();
27961         }
27962     },
27963
27964     // private
27965     onClick : function(e){
27966         e.preventDefault();
27967         if(!this.disabled){
27968             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27969                 if(this.menu && !this.menu.isVisible()){
27970                     this.menu.show(this.el, this.menuAlign);
27971                 }
27972                 this.fireEvent("arrowclick", this, e);
27973                 if(this.arrowHandler){
27974                     this.arrowHandler.call(this.scope || this, this, e);
27975                 }
27976             }else{
27977                 this.fireEvent("click", this, e);
27978                 if(this.handler){
27979                     this.handler.call(this.scope || this, this, e);
27980                 }
27981             }
27982         }
27983     },
27984     // private
27985     onMouseDown : function(e){
27986         if(!this.disabled){
27987             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27988         }
27989     },
27990     // private
27991     onMouseUp : function(e){
27992         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27993     }   
27994 });
27995
27996
27997 // backwards compat
27998 Roo.MenuButton = Roo.SplitButton;/*
27999  * Based on:
28000  * Ext JS Library 1.1.1
28001  * Copyright(c) 2006-2007, Ext JS, LLC.
28002  *
28003  * Originally Released Under LGPL - original licence link has changed is not relivant.
28004  *
28005  * Fork - LGPL
28006  * <script type="text/javascript">
28007  */
28008
28009 /**
28010  * @class Roo.Toolbar
28011  * Basic Toolbar class.
28012  * @constructor
28013  * Creates a new Toolbar
28014  * @param {Object} container The config object
28015  */ 
28016 Roo.Toolbar = function(container, buttons, config)
28017 {
28018     /// old consturctor format still supported..
28019     if(container instanceof Array){ // omit the container for later rendering
28020         buttons = container;
28021         config = buttons;
28022         container = null;
28023     }
28024     if (typeof(container) == 'object' && container.xtype) {
28025         config = container;
28026         container = config.container;
28027         buttons = config.buttons || []; // not really - use items!!
28028     }
28029     var xitems = [];
28030     if (config && config.items) {
28031         xitems = config.items;
28032         delete config.items;
28033     }
28034     Roo.apply(this, config);
28035     this.buttons = buttons;
28036     
28037     if(container){
28038         this.render(container);
28039     }
28040     this.xitems = xitems;
28041     Roo.each(xitems, function(b) {
28042         this.add(b);
28043     }, this);
28044     
28045 };
28046
28047 Roo.Toolbar.prototype = {
28048     /**
28049      * @cfg {Array} items
28050      * array of button configs or elements to add (will be converted to a MixedCollection)
28051      */
28052     
28053     /**
28054      * @cfg {String/HTMLElement/Element} container
28055      * The id or element that will contain the toolbar
28056      */
28057     // private
28058     render : function(ct){
28059         this.el = Roo.get(ct);
28060         if(this.cls){
28061             this.el.addClass(this.cls);
28062         }
28063         // using a table allows for vertical alignment
28064         // 100% width is needed by Safari...
28065         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28066         this.tr = this.el.child("tr", true);
28067         var autoId = 0;
28068         this.items = new Roo.util.MixedCollection(false, function(o){
28069             return o.id || ("item" + (++autoId));
28070         });
28071         if(this.buttons){
28072             this.add.apply(this, this.buttons);
28073             delete this.buttons;
28074         }
28075     },
28076
28077     /**
28078      * Adds element(s) to the toolbar -- this function takes a variable number of 
28079      * arguments of mixed type and adds them to the toolbar.
28080      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28081      * <ul>
28082      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28083      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28084      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28085      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28086      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28087      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28088      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28089      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28090      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28091      * </ul>
28092      * @param {Mixed} arg2
28093      * @param {Mixed} etc.
28094      */
28095     add : function(){
28096         var a = arguments, l = a.length;
28097         for(var i = 0; i < l; i++){
28098             this._add(a[i]);
28099         }
28100     },
28101     // private..
28102     _add : function(el) {
28103         
28104         if (el.xtype) {
28105             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28106         }
28107         
28108         if (el.applyTo){ // some kind of form field
28109             return this.addField(el);
28110         } 
28111         if (el.render){ // some kind of Toolbar.Item
28112             return this.addItem(el);
28113         }
28114         if (typeof el == "string"){ // string
28115             if(el == "separator" || el == "-"){
28116                 return this.addSeparator();
28117             }
28118             if (el == " "){
28119                 return this.addSpacer();
28120             }
28121             if(el == "->"){
28122                 return this.addFill();
28123             }
28124             return this.addText(el);
28125             
28126         }
28127         if(el.tagName){ // element
28128             return this.addElement(el);
28129         }
28130         if(typeof el == "object"){ // must be button config?
28131             return this.addButton(el);
28132         }
28133         // and now what?!?!
28134         return false;
28135         
28136     },
28137     
28138     /**
28139      * Add an Xtype element
28140      * @param {Object} xtype Xtype Object
28141      * @return {Object} created Object
28142      */
28143     addxtype : function(e){
28144         return this.add(e);  
28145     },
28146     
28147     /**
28148      * Returns the Element for this toolbar.
28149      * @return {Roo.Element}
28150      */
28151     getEl : function(){
28152         return this.el;  
28153     },
28154     
28155     /**
28156      * Adds a separator
28157      * @return {Roo.Toolbar.Item} The separator item
28158      */
28159     addSeparator : function(){
28160         return this.addItem(new Roo.Toolbar.Separator());
28161     },
28162
28163     /**
28164      * Adds a spacer element
28165      * @return {Roo.Toolbar.Spacer} The spacer item
28166      */
28167     addSpacer : function(){
28168         return this.addItem(new Roo.Toolbar.Spacer());
28169     },
28170
28171     /**
28172      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28173      * @return {Roo.Toolbar.Fill} The fill item
28174      */
28175     addFill : function(){
28176         return this.addItem(new Roo.Toolbar.Fill());
28177     },
28178
28179     /**
28180      * Adds any standard HTML element to the toolbar
28181      * @param {String/HTMLElement/Element} el The element or id of the element to add
28182      * @return {Roo.Toolbar.Item} The element's item
28183      */
28184     addElement : function(el){
28185         return this.addItem(new Roo.Toolbar.Item(el));
28186     },
28187     /**
28188      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28189      * @type Roo.util.MixedCollection  
28190      */
28191     items : false,
28192      
28193     /**
28194      * Adds any Toolbar.Item or subclass
28195      * @param {Roo.Toolbar.Item} item
28196      * @return {Roo.Toolbar.Item} The item
28197      */
28198     addItem : function(item){
28199         var td = this.nextBlock();
28200         item.render(td);
28201         this.items.add(item);
28202         return item;
28203     },
28204     
28205     /**
28206      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28207      * @param {Object/Array} config A button config or array of configs
28208      * @return {Roo.Toolbar.Button/Array}
28209      */
28210     addButton : function(config){
28211         if(config instanceof Array){
28212             var buttons = [];
28213             for(var i = 0, len = config.length; i < len; i++) {
28214                 buttons.push(this.addButton(config[i]));
28215             }
28216             return buttons;
28217         }
28218         var b = config;
28219         if(!(config instanceof Roo.Toolbar.Button)){
28220             b = config.split ?
28221                 new Roo.Toolbar.SplitButton(config) :
28222                 new Roo.Toolbar.Button(config);
28223         }
28224         var td = this.nextBlock();
28225         b.render(td);
28226         this.items.add(b);
28227         return b;
28228     },
28229     
28230     /**
28231      * Adds text to the toolbar
28232      * @param {String} text The text to add
28233      * @return {Roo.Toolbar.Item} The element's item
28234      */
28235     addText : function(text){
28236         return this.addItem(new Roo.Toolbar.TextItem(text));
28237     },
28238     
28239     /**
28240      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28241      * @param {Number} index The index where the item is to be inserted
28242      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28243      * @return {Roo.Toolbar.Button/Item}
28244      */
28245     insertButton : function(index, item){
28246         if(item instanceof Array){
28247             var buttons = [];
28248             for(var i = 0, len = item.length; i < len; i++) {
28249                buttons.push(this.insertButton(index + i, item[i]));
28250             }
28251             return buttons;
28252         }
28253         if (!(item instanceof Roo.Toolbar.Button)){
28254            item = new Roo.Toolbar.Button(item);
28255         }
28256         var td = document.createElement("td");
28257         this.tr.insertBefore(td, this.tr.childNodes[index]);
28258         item.render(td);
28259         this.items.insert(index, item);
28260         return item;
28261     },
28262     
28263     /**
28264      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28265      * @param {Object} config
28266      * @return {Roo.Toolbar.Item} The element's item
28267      */
28268     addDom : function(config, returnEl){
28269         var td = this.nextBlock();
28270         Roo.DomHelper.overwrite(td, config);
28271         var ti = new Roo.Toolbar.Item(td.firstChild);
28272         ti.render(td);
28273         this.items.add(ti);
28274         return ti;
28275     },
28276
28277     /**
28278      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28279      * @type Roo.util.MixedCollection  
28280      */
28281     fields : false,
28282     
28283     /**
28284      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28285      * Note: the field should not have been rendered yet. For a field that has already been
28286      * rendered, use {@link #addElement}.
28287      * @param {Roo.form.Field} field
28288      * @return {Roo.ToolbarItem}
28289      */
28290      
28291       
28292     addField : function(field) {
28293         if (!this.fields) {
28294             var autoId = 0;
28295             this.fields = new Roo.util.MixedCollection(false, function(o){
28296                 return o.id || ("item" + (++autoId));
28297             });
28298
28299         }
28300         
28301         var td = this.nextBlock();
28302         field.render(td);
28303         var ti = new Roo.Toolbar.Item(td.firstChild);
28304         ti.render(td);
28305         this.items.add(ti);
28306         this.fields.add(field);
28307         return ti;
28308     },
28309     /**
28310      * Hide the toolbar
28311      * @method hide
28312      */
28313      
28314       
28315     hide : function()
28316     {
28317         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28318         this.el.child('div').hide();
28319     },
28320     /**
28321      * Show the toolbar
28322      * @method show
28323      */
28324     show : function()
28325     {
28326         this.el.child('div').show();
28327     },
28328       
28329     // private
28330     nextBlock : function(){
28331         var td = document.createElement("td");
28332         this.tr.appendChild(td);
28333         return td;
28334     },
28335
28336     // private
28337     destroy : function(){
28338         if(this.items){ // rendered?
28339             Roo.destroy.apply(Roo, this.items.items);
28340         }
28341         if(this.fields){ // rendered?
28342             Roo.destroy.apply(Roo, this.fields.items);
28343         }
28344         Roo.Element.uncache(this.el, this.tr);
28345     }
28346 };
28347
28348 /**
28349  * @class Roo.Toolbar.Item
28350  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28351  * @constructor
28352  * Creates a new Item
28353  * @param {HTMLElement} el 
28354  */
28355 Roo.Toolbar.Item = function(el){
28356     this.el = Roo.getDom(el);
28357     this.id = Roo.id(this.el);
28358     this.hidden = false;
28359 };
28360
28361 Roo.Toolbar.Item.prototype = {
28362     
28363     /**
28364      * Get this item's HTML Element
28365      * @return {HTMLElement}
28366      */
28367     getEl : function(){
28368        return this.el;  
28369     },
28370
28371     // private
28372     render : function(td){
28373         this.td = td;
28374         td.appendChild(this.el);
28375     },
28376     
28377     /**
28378      * Removes and destroys this item.
28379      */
28380     destroy : function(){
28381         this.td.parentNode.removeChild(this.td);
28382     },
28383     
28384     /**
28385      * Shows this item.
28386      */
28387     show: function(){
28388         this.hidden = false;
28389         this.td.style.display = "";
28390     },
28391     
28392     /**
28393      * Hides this item.
28394      */
28395     hide: function(){
28396         this.hidden = true;
28397         this.td.style.display = "none";
28398     },
28399     
28400     /**
28401      * Convenience function for boolean show/hide.
28402      * @param {Boolean} visible true to show/false to hide
28403      */
28404     setVisible: function(visible){
28405         if(visible) {
28406             this.show();
28407         }else{
28408             this.hide();
28409         }
28410     },
28411     
28412     /**
28413      * Try to focus this item.
28414      */
28415     focus : function(){
28416         Roo.fly(this.el).focus();
28417     },
28418     
28419     /**
28420      * Disables this item.
28421      */
28422     disable : function(){
28423         Roo.fly(this.td).addClass("x-item-disabled");
28424         this.disabled = true;
28425         this.el.disabled = true;
28426     },
28427     
28428     /**
28429      * Enables this item.
28430      */
28431     enable : function(){
28432         Roo.fly(this.td).removeClass("x-item-disabled");
28433         this.disabled = false;
28434         this.el.disabled = false;
28435     }
28436 };
28437
28438
28439 /**
28440  * @class Roo.Toolbar.Separator
28441  * @extends Roo.Toolbar.Item
28442  * A simple toolbar separator class
28443  * @constructor
28444  * Creates a new Separator
28445  */
28446 Roo.Toolbar.Separator = function(){
28447     var s = document.createElement("span");
28448     s.className = "ytb-sep";
28449     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28450 };
28451 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28452     enable:Roo.emptyFn,
28453     disable:Roo.emptyFn,
28454     focus:Roo.emptyFn
28455 });
28456
28457 /**
28458  * @class Roo.Toolbar.Spacer
28459  * @extends Roo.Toolbar.Item
28460  * A simple element that adds extra horizontal space to a toolbar.
28461  * @constructor
28462  * Creates a new Spacer
28463  */
28464 Roo.Toolbar.Spacer = function(){
28465     var s = document.createElement("div");
28466     s.className = "ytb-spacer";
28467     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28468 };
28469 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28470     enable:Roo.emptyFn,
28471     disable:Roo.emptyFn,
28472     focus:Roo.emptyFn
28473 });
28474
28475 /**
28476  * @class Roo.Toolbar.Fill
28477  * @extends Roo.Toolbar.Spacer
28478  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28479  * @constructor
28480  * Creates a new Spacer
28481  */
28482 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28483     // private
28484     render : function(td){
28485         td.style.width = '100%';
28486         Roo.Toolbar.Fill.superclass.render.call(this, td);
28487     }
28488 });
28489
28490 /**
28491  * @class Roo.Toolbar.TextItem
28492  * @extends Roo.Toolbar.Item
28493  * A simple class that renders text directly into a toolbar.
28494  * @constructor
28495  * Creates a new TextItem
28496  * @param {String} text
28497  */
28498 Roo.Toolbar.TextItem = function(text){
28499     if (typeof(text) == 'object') {
28500         text = text.text;
28501     }
28502     var s = document.createElement("span");
28503     s.className = "ytb-text";
28504     s.innerHTML = text;
28505     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28506 };
28507 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28508     enable:Roo.emptyFn,
28509     disable:Roo.emptyFn,
28510     focus:Roo.emptyFn
28511 });
28512
28513 /**
28514  * @class Roo.Toolbar.Button
28515  * @extends Roo.Button
28516  * A button that renders into a toolbar.
28517  * @constructor
28518  * Creates a new Button
28519  * @param {Object} config A standard {@link Roo.Button} config object
28520  */
28521 Roo.Toolbar.Button = function(config){
28522     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28523 };
28524 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28525     render : function(td){
28526         this.td = td;
28527         Roo.Toolbar.Button.superclass.render.call(this, td);
28528     },
28529     
28530     /**
28531      * Removes and destroys this button
28532      */
28533     destroy : function(){
28534         Roo.Toolbar.Button.superclass.destroy.call(this);
28535         this.td.parentNode.removeChild(this.td);
28536     },
28537     
28538     /**
28539      * Shows this button
28540      */
28541     show: function(){
28542         this.hidden = false;
28543         this.td.style.display = "";
28544     },
28545     
28546     /**
28547      * Hides this button
28548      */
28549     hide: function(){
28550         this.hidden = true;
28551         this.td.style.display = "none";
28552     },
28553
28554     /**
28555      * Disables this item
28556      */
28557     disable : function(){
28558         Roo.fly(this.td).addClass("x-item-disabled");
28559         this.disabled = true;
28560     },
28561
28562     /**
28563      * Enables this item
28564      */
28565     enable : function(){
28566         Roo.fly(this.td).removeClass("x-item-disabled");
28567         this.disabled = false;
28568     }
28569 });
28570 // backwards compat
28571 Roo.ToolbarButton = Roo.Toolbar.Button;
28572
28573 /**
28574  * @class Roo.Toolbar.SplitButton
28575  * @extends Roo.SplitButton
28576  * A menu button that renders into a toolbar.
28577  * @constructor
28578  * Creates a new SplitButton
28579  * @param {Object} config A standard {@link Roo.SplitButton} config object
28580  */
28581 Roo.Toolbar.SplitButton = function(config){
28582     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28583 };
28584 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28585     render : function(td){
28586         this.td = td;
28587         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28588     },
28589     
28590     /**
28591      * Removes and destroys this button
28592      */
28593     destroy : function(){
28594         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28595         this.td.parentNode.removeChild(this.td);
28596     },
28597     
28598     /**
28599      * Shows this button
28600      */
28601     show: function(){
28602         this.hidden = false;
28603         this.td.style.display = "";
28604     },
28605     
28606     /**
28607      * Hides this button
28608      */
28609     hide: function(){
28610         this.hidden = true;
28611         this.td.style.display = "none";
28612     }
28613 });
28614
28615 // backwards compat
28616 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28617  * Based on:
28618  * Ext JS Library 1.1.1
28619  * Copyright(c) 2006-2007, Ext JS, LLC.
28620  *
28621  * Originally Released Under LGPL - original licence link has changed is not relivant.
28622  *
28623  * Fork - LGPL
28624  * <script type="text/javascript">
28625  */
28626  
28627 /**
28628  * @class Roo.PagingToolbar
28629  * @extends Roo.Toolbar
28630  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28631  * @constructor
28632  * Create a new PagingToolbar
28633  * @param {Object} config The config object
28634  */
28635 Roo.PagingToolbar = function(el, ds, config)
28636 {
28637     // old args format still supported... - xtype is prefered..
28638     if (typeof(el) == 'object' && el.xtype) {
28639         // created from xtype...
28640         config = el;
28641         ds = el.dataSource;
28642         el = config.container;
28643     }
28644     var items = [];
28645     if (config.items) {
28646         items = config.items;
28647         config.items = [];
28648     }
28649     
28650     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28651     this.ds = ds;
28652     this.cursor = 0;
28653     this.renderButtons(this.el);
28654     this.bind(ds);
28655     
28656     // supprot items array.
28657    
28658     Roo.each(items, function(e) {
28659         this.add(Roo.factory(e));
28660     },this);
28661     
28662 };
28663
28664 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28665     /**
28666      * @cfg {Roo.data.Store} dataSource
28667      * The underlying data store providing the paged data
28668      */
28669     /**
28670      * @cfg {String/HTMLElement/Element} container
28671      * container The id or element that will contain the toolbar
28672      */
28673     /**
28674      * @cfg {Boolean} displayInfo
28675      * True to display the displayMsg (defaults to false)
28676      */
28677     /**
28678      * @cfg {Number} pageSize
28679      * The number of records to display per page (defaults to 20)
28680      */
28681     pageSize: 20,
28682     /**
28683      * @cfg {String} displayMsg
28684      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28685      */
28686     displayMsg : 'Displaying {0} - {1} of {2}',
28687     /**
28688      * @cfg {String} emptyMsg
28689      * The message to display when no records are found (defaults to "No data to display")
28690      */
28691     emptyMsg : 'No data to display',
28692     /**
28693      * Customizable piece of the default paging text (defaults to "Page")
28694      * @type String
28695      */
28696     beforePageText : "Page",
28697     /**
28698      * Customizable piece of the default paging text (defaults to "of %0")
28699      * @type String
28700      */
28701     afterPageText : "of {0}",
28702     /**
28703      * Customizable piece of the default paging text (defaults to "First Page")
28704      * @type String
28705      */
28706     firstText : "First Page",
28707     /**
28708      * Customizable piece of the default paging text (defaults to "Previous Page")
28709      * @type String
28710      */
28711     prevText : "Previous Page",
28712     /**
28713      * Customizable piece of the default paging text (defaults to "Next Page")
28714      * @type String
28715      */
28716     nextText : "Next Page",
28717     /**
28718      * Customizable piece of the default paging text (defaults to "Last Page")
28719      * @type String
28720      */
28721     lastText : "Last Page",
28722     /**
28723      * Customizable piece of the default paging text (defaults to "Refresh")
28724      * @type String
28725      */
28726     refreshText : "Refresh",
28727
28728     // private
28729     renderButtons : function(el){
28730         Roo.PagingToolbar.superclass.render.call(this, el);
28731         this.first = this.addButton({
28732             tooltip: this.firstText,
28733             cls: "x-btn-icon x-grid-page-first",
28734             disabled: true,
28735             handler: this.onClick.createDelegate(this, ["first"])
28736         });
28737         this.prev = this.addButton({
28738             tooltip: this.prevText,
28739             cls: "x-btn-icon x-grid-page-prev",
28740             disabled: true,
28741             handler: this.onClick.createDelegate(this, ["prev"])
28742         });
28743         //this.addSeparator();
28744         this.add(this.beforePageText);
28745         this.field = Roo.get(this.addDom({
28746            tag: "input",
28747            type: "text",
28748            size: "3",
28749            value: "1",
28750            cls: "x-grid-page-number"
28751         }).el);
28752         this.field.on("keydown", this.onPagingKeydown, this);
28753         this.field.on("focus", function(){this.dom.select();});
28754         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28755         this.field.setHeight(18);
28756         //this.addSeparator();
28757         this.next = this.addButton({
28758             tooltip: this.nextText,
28759             cls: "x-btn-icon x-grid-page-next",
28760             disabled: true,
28761             handler: this.onClick.createDelegate(this, ["next"])
28762         });
28763         this.last = this.addButton({
28764             tooltip: this.lastText,
28765             cls: "x-btn-icon x-grid-page-last",
28766             disabled: true,
28767             handler: this.onClick.createDelegate(this, ["last"])
28768         });
28769         //this.addSeparator();
28770         this.loading = this.addButton({
28771             tooltip: this.refreshText,
28772             cls: "x-btn-icon x-grid-loading",
28773             handler: this.onClick.createDelegate(this, ["refresh"])
28774         });
28775
28776         if(this.displayInfo){
28777             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28778         }
28779     },
28780
28781     // private
28782     updateInfo : function(){
28783         if(this.displayEl){
28784             var count = this.ds.getCount();
28785             var msg = count == 0 ?
28786                 this.emptyMsg :
28787                 String.format(
28788                     this.displayMsg,
28789                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28790                 );
28791             this.displayEl.update(msg);
28792         }
28793     },
28794
28795     // private
28796     onLoad : function(ds, r, o){
28797        this.cursor = o.params ? o.params.start : 0;
28798        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28799
28800        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28801        this.field.dom.value = ap;
28802        this.first.setDisabled(ap == 1);
28803        this.prev.setDisabled(ap == 1);
28804        this.next.setDisabled(ap == ps);
28805        this.last.setDisabled(ap == ps);
28806        this.loading.enable();
28807        this.updateInfo();
28808     },
28809
28810     // private
28811     getPageData : function(){
28812         var total = this.ds.getTotalCount();
28813         return {
28814             total : total,
28815             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28816             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28817         };
28818     },
28819
28820     // private
28821     onLoadError : function(){
28822         this.loading.enable();
28823     },
28824
28825     // private
28826     onPagingKeydown : function(e){
28827         var k = e.getKey();
28828         var d = this.getPageData();
28829         if(k == e.RETURN){
28830             var v = this.field.dom.value, pageNum;
28831             if(!v || isNaN(pageNum = parseInt(v, 10))){
28832                 this.field.dom.value = d.activePage;
28833                 return;
28834             }
28835             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28836             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28837             e.stopEvent();
28838         }
28839         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))
28840         {
28841           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28842           this.field.dom.value = pageNum;
28843           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28844           e.stopEvent();
28845         }
28846         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28847         {
28848           var v = this.field.dom.value, pageNum; 
28849           var increment = (e.shiftKey) ? 10 : 1;
28850           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28851             increment *= -1;
28852           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28853             this.field.dom.value = d.activePage;
28854             return;
28855           }
28856           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28857           {
28858             this.field.dom.value = parseInt(v, 10) + increment;
28859             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28860             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28861           }
28862           e.stopEvent();
28863         }
28864     },
28865
28866     // private
28867     beforeLoad : function(){
28868         if(this.loading){
28869             this.loading.disable();
28870         }
28871     },
28872
28873     // private
28874     onClick : function(which){
28875         var ds = this.ds;
28876         switch(which){
28877             case "first":
28878                 ds.load({params:{start: 0, limit: this.pageSize}});
28879             break;
28880             case "prev":
28881                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28882             break;
28883             case "next":
28884                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28885             break;
28886             case "last":
28887                 var total = ds.getTotalCount();
28888                 var extra = total % this.pageSize;
28889                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28890                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28891             break;
28892             case "refresh":
28893                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28894             break;
28895         }
28896     },
28897
28898     /**
28899      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28900      * @param {Roo.data.Store} store The data store to unbind
28901      */
28902     unbind : function(ds){
28903         ds.un("beforeload", this.beforeLoad, this);
28904         ds.un("load", this.onLoad, this);
28905         ds.un("loadexception", this.onLoadError, this);
28906         ds.un("remove", this.updateInfo, this);
28907         ds.un("add", this.updateInfo, this);
28908         this.ds = undefined;
28909     },
28910
28911     /**
28912      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28913      * @param {Roo.data.Store} store The data store to bind
28914      */
28915     bind : function(ds){
28916         ds.on("beforeload", this.beforeLoad, this);
28917         ds.on("load", this.onLoad, this);
28918         ds.on("loadexception", this.onLoadError, this);
28919         ds.on("remove", this.updateInfo, this);
28920         ds.on("add", this.updateInfo, this);
28921         this.ds = ds;
28922     }
28923 });/*
28924  * Based on:
28925  * Ext JS Library 1.1.1
28926  * Copyright(c) 2006-2007, Ext JS, LLC.
28927  *
28928  * Originally Released Under LGPL - original licence link has changed is not relivant.
28929  *
28930  * Fork - LGPL
28931  * <script type="text/javascript">
28932  */
28933
28934 /**
28935  * @class Roo.Resizable
28936  * @extends Roo.util.Observable
28937  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28938  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28939  * 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
28940  * the element will be wrapped for you automatically.</p>
28941  * <p>Here is the list of valid resize handles:</p>
28942  * <pre>
28943 Value   Description
28944 ------  -------------------
28945  'n'     north
28946  's'     south
28947  'e'     east
28948  'w'     west
28949  'nw'    northwest
28950  'sw'    southwest
28951  'se'    southeast
28952  'ne'    northeast
28953  'hd'    horizontal drag
28954  'all'   all
28955 </pre>
28956  * <p>Here's an example showing the creation of a typical Resizable:</p>
28957  * <pre><code>
28958 var resizer = new Roo.Resizable("element-id", {
28959     handles: 'all',
28960     minWidth: 200,
28961     minHeight: 100,
28962     maxWidth: 500,
28963     maxHeight: 400,
28964     pinned: true
28965 });
28966 resizer.on("resize", myHandler);
28967 </code></pre>
28968  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28969  * resizer.east.setDisplayed(false);</p>
28970  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28971  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28972  * resize operation's new size (defaults to [0, 0])
28973  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28974  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28975  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28976  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28977  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28978  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28979  * @cfg {Number} width The width of the element in pixels (defaults to null)
28980  * @cfg {Number} height The height of the element in pixels (defaults to null)
28981  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28982  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28983  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28984  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28985  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28986  * in favor of the handles config option (defaults to false)
28987  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28988  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28989  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28990  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28991  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28992  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28993  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28994  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28995  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28996  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28997  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28998  * @constructor
28999  * Create a new resizable component
29000  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29001  * @param {Object} config configuration options
29002   */
29003 Roo.Resizable = function(el, config)
29004 {
29005     this.el = Roo.get(el);
29006
29007     if(config && config.wrap){
29008         config.resizeChild = this.el;
29009         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29010         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29011         this.el.setStyle("overflow", "hidden");
29012         this.el.setPositioning(config.resizeChild.getPositioning());
29013         config.resizeChild.clearPositioning();
29014         if(!config.width || !config.height){
29015             var csize = config.resizeChild.getSize();
29016             this.el.setSize(csize.width, csize.height);
29017         }
29018         if(config.pinned && !config.adjustments){
29019             config.adjustments = "auto";
29020         }
29021     }
29022
29023     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29024     this.proxy.unselectable();
29025     this.proxy.enableDisplayMode('block');
29026
29027     Roo.apply(this, config);
29028
29029     if(this.pinned){
29030         this.disableTrackOver = true;
29031         this.el.addClass("x-resizable-pinned");
29032     }
29033     // if the element isn't positioned, make it relative
29034     var position = this.el.getStyle("position");
29035     if(position != "absolute" && position != "fixed"){
29036         this.el.setStyle("position", "relative");
29037     }
29038     if(!this.handles){ // no handles passed, must be legacy style
29039         this.handles = 's,e,se';
29040         if(this.multiDirectional){
29041             this.handles += ',n,w';
29042         }
29043     }
29044     if(this.handles == "all"){
29045         this.handles = "n s e w ne nw se sw";
29046     }
29047     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29048     var ps = Roo.Resizable.positions;
29049     for(var i = 0, len = hs.length; i < len; i++){
29050         if(hs[i] && ps[hs[i]]){
29051             var pos = ps[hs[i]];
29052             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29053         }
29054     }
29055     // legacy
29056     this.corner = this.southeast;
29057     
29058     // updateBox = the box can move..
29059     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29060         this.updateBox = true;
29061     }
29062
29063     this.activeHandle = null;
29064
29065     if(this.resizeChild){
29066         if(typeof this.resizeChild == "boolean"){
29067             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29068         }else{
29069             this.resizeChild = Roo.get(this.resizeChild, true);
29070         }
29071     }
29072     
29073     if(this.adjustments == "auto"){
29074         var rc = this.resizeChild;
29075         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29076         if(rc && (hw || hn)){
29077             rc.position("relative");
29078             rc.setLeft(hw ? hw.el.getWidth() : 0);
29079             rc.setTop(hn ? hn.el.getHeight() : 0);
29080         }
29081         this.adjustments = [
29082             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29083             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29084         ];
29085     }
29086
29087     if(this.draggable){
29088         this.dd = this.dynamic ?
29089             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29090         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29091     }
29092
29093     // public events
29094     this.addEvents({
29095         /**
29096          * @event beforeresize
29097          * Fired before resize is allowed. Set enabled to false to cancel resize.
29098          * @param {Roo.Resizable} this
29099          * @param {Roo.EventObject} e The mousedown event
29100          */
29101         "beforeresize" : true,
29102         /**
29103          * @event resizing
29104          * Fired a resizing.
29105          * @param {Roo.Resizable} this
29106          * @param {Number} x The new x position
29107          * @param {Number} y The new y position
29108          * @param {Number} w The new w width
29109          * @param {Number} h The new h hight
29110          * @param {Roo.EventObject} e The mouseup event
29111          */
29112         "resizing" : true,
29113         /**
29114          * @event resize
29115          * Fired after a resize.
29116          * @param {Roo.Resizable} this
29117          * @param {Number} width The new width
29118          * @param {Number} height The new height
29119          * @param {Roo.EventObject} e The mouseup event
29120          */
29121         "resize" : true
29122     });
29123
29124     if(this.width !== null && this.height !== null){
29125         this.resizeTo(this.width, this.height);
29126     }else{
29127         this.updateChildSize();
29128     }
29129     if(Roo.isIE){
29130         this.el.dom.style.zoom = 1;
29131     }
29132     Roo.Resizable.superclass.constructor.call(this);
29133 };
29134
29135 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29136         resizeChild : false,
29137         adjustments : [0, 0],
29138         minWidth : 5,
29139         minHeight : 5,
29140         maxWidth : 10000,
29141         maxHeight : 10000,
29142         enabled : true,
29143         animate : false,
29144         duration : .35,
29145         dynamic : false,
29146         handles : false,
29147         multiDirectional : false,
29148         disableTrackOver : false,
29149         easing : 'easeOutStrong',
29150         widthIncrement : 0,
29151         heightIncrement : 0,
29152         pinned : false,
29153         width : null,
29154         height : null,
29155         preserveRatio : false,
29156         transparent: false,
29157         minX: 0,
29158         minY: 0,
29159         draggable: false,
29160
29161         /**
29162          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29163          */
29164         constrainTo: undefined,
29165         /**
29166          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29167          */
29168         resizeRegion: undefined,
29169
29170
29171     /**
29172      * Perform a manual resize
29173      * @param {Number} width
29174      * @param {Number} height
29175      */
29176     resizeTo : function(width, height){
29177         this.el.setSize(width, height);
29178         this.updateChildSize();
29179         this.fireEvent("resize", this, width, height, null);
29180     },
29181
29182     // private
29183     startSizing : function(e, handle){
29184         this.fireEvent("beforeresize", this, e);
29185         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29186
29187             if(!this.overlay){
29188                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29189                 this.overlay.unselectable();
29190                 this.overlay.enableDisplayMode("block");
29191                 this.overlay.on("mousemove", this.onMouseMove, this);
29192                 this.overlay.on("mouseup", this.onMouseUp, this);
29193             }
29194             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29195
29196             this.resizing = true;
29197             this.startBox = this.el.getBox();
29198             this.startPoint = e.getXY();
29199             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29200                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29201
29202             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29203             this.overlay.show();
29204
29205             if(this.constrainTo) {
29206                 var ct = Roo.get(this.constrainTo);
29207                 this.resizeRegion = ct.getRegion().adjust(
29208                     ct.getFrameWidth('t'),
29209                     ct.getFrameWidth('l'),
29210                     -ct.getFrameWidth('b'),
29211                     -ct.getFrameWidth('r')
29212                 );
29213             }
29214
29215             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29216             this.proxy.show();
29217             this.proxy.setBox(this.startBox);
29218             if(!this.dynamic){
29219                 this.proxy.setStyle('visibility', 'visible');
29220             }
29221         }
29222     },
29223
29224     // private
29225     onMouseDown : function(handle, e){
29226         if(this.enabled){
29227             e.stopEvent();
29228             this.activeHandle = handle;
29229             this.startSizing(e, handle);
29230         }
29231     },
29232
29233     // private
29234     onMouseUp : function(e){
29235         var size = this.resizeElement();
29236         this.resizing = false;
29237         this.handleOut();
29238         this.overlay.hide();
29239         this.proxy.hide();
29240         this.fireEvent("resize", this, size.width, size.height, e);
29241     },
29242
29243     // private
29244     updateChildSize : function(){
29245         
29246         if(this.resizeChild){
29247             var el = this.el;
29248             var child = this.resizeChild;
29249             var adj = this.adjustments;
29250             if(el.dom.offsetWidth){
29251                 var b = el.getSize(true);
29252                 child.setSize(b.width+adj[0], b.height+adj[1]);
29253             }
29254             // Second call here for IE
29255             // The first call enables instant resizing and
29256             // the second call corrects scroll bars if they
29257             // exist
29258             if(Roo.isIE){
29259                 setTimeout(function(){
29260                     if(el.dom.offsetWidth){
29261                         var b = el.getSize(true);
29262                         child.setSize(b.width+adj[0], b.height+adj[1]);
29263                     }
29264                 }, 10);
29265             }
29266         }
29267     },
29268
29269     // private
29270     snap : function(value, inc, min){
29271         if(!inc || !value) return value;
29272         var newValue = value;
29273         var m = value % inc;
29274         if(m > 0){
29275             if(m > (inc/2)){
29276                 newValue = value + (inc-m);
29277             }else{
29278                 newValue = value - m;
29279             }
29280         }
29281         return Math.max(min, newValue);
29282     },
29283
29284     // private
29285     resizeElement : function(){
29286         var box = this.proxy.getBox();
29287         if(this.updateBox){
29288             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29289         }else{
29290             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29291         }
29292         this.updateChildSize();
29293         if(!this.dynamic){
29294             this.proxy.hide();
29295         }
29296         return box;
29297     },
29298
29299     // private
29300     constrain : function(v, diff, m, mx){
29301         if(v - diff < m){
29302             diff = v - m;
29303         }else if(v - diff > mx){
29304             diff = mx - v;
29305         }
29306         return diff;
29307     },
29308
29309     // private
29310     onMouseMove : function(e){
29311         
29312         if(this.enabled){
29313             try{// try catch so if something goes wrong the user doesn't get hung
29314
29315             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29316                 return;
29317             }
29318
29319             //var curXY = this.startPoint;
29320             var curSize = this.curSize || this.startBox;
29321             var x = this.startBox.x, y = this.startBox.y;
29322             var ox = x, oy = y;
29323             var w = curSize.width, h = curSize.height;
29324             var ow = w, oh = h;
29325             var mw = this.minWidth, mh = this.minHeight;
29326             var mxw = this.maxWidth, mxh = this.maxHeight;
29327             var wi = this.widthIncrement;
29328             var hi = this.heightIncrement;
29329
29330             var eventXY = e.getXY();
29331             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29332             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29333
29334             var pos = this.activeHandle.position;
29335
29336             switch(pos){
29337                 case "east":
29338                     w += diffX;
29339                     w = Math.min(Math.max(mw, w), mxw);
29340                     break;
29341              
29342                 case "south":
29343                     h += diffY;
29344                     h = Math.min(Math.max(mh, h), mxh);
29345                     break;
29346                 case "southeast":
29347                     w += diffX;
29348                     h += diffY;
29349                     w = Math.min(Math.max(mw, w), mxw);
29350                     h = Math.min(Math.max(mh, h), mxh);
29351                     break;
29352                 case "north":
29353                     diffY = this.constrain(h, diffY, mh, mxh);
29354                     y += diffY;
29355                     h -= diffY;
29356                     break;
29357                 case "hdrag":
29358                     
29359                     if (wi) {
29360                         var adiffX = Math.abs(diffX);
29361                         var sub = (adiffX % wi); // how much 
29362                         if (sub > (wi/2)) { // far enough to snap
29363                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29364                         } else {
29365                             // remove difference.. 
29366                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29367                         }
29368                     }
29369                     x += diffX;
29370                     x = Math.max(this.minX, x);
29371                     break;
29372                 case "west":
29373                     diffX = this.constrain(w, diffX, mw, mxw);
29374                     x += diffX;
29375                     w -= diffX;
29376                     break;
29377                 case "northeast":
29378                     w += diffX;
29379                     w = Math.min(Math.max(mw, w), mxw);
29380                     diffY = this.constrain(h, diffY, mh, mxh);
29381                     y += diffY;
29382                     h -= diffY;
29383                     break;
29384                 case "northwest":
29385                     diffX = this.constrain(w, diffX, mw, mxw);
29386                     diffY = this.constrain(h, diffY, mh, mxh);
29387                     y += diffY;
29388                     h -= diffY;
29389                     x += diffX;
29390                     w -= diffX;
29391                     break;
29392                case "southwest":
29393                     diffX = this.constrain(w, diffX, mw, mxw);
29394                     h += diffY;
29395                     h = Math.min(Math.max(mh, h), mxh);
29396                     x += diffX;
29397                     w -= diffX;
29398                     break;
29399             }
29400
29401             var sw = this.snap(w, wi, mw);
29402             var sh = this.snap(h, hi, mh);
29403             if(sw != w || sh != h){
29404                 switch(pos){
29405                     case "northeast":
29406                         y -= sh - h;
29407                     break;
29408                     case "north":
29409                         y -= sh - h;
29410                         break;
29411                     case "southwest":
29412                         x -= sw - w;
29413                     break;
29414                     case "west":
29415                         x -= sw - w;
29416                         break;
29417                     case "northwest":
29418                         x -= sw - w;
29419                         y -= sh - h;
29420                     break;
29421                 }
29422                 w = sw;
29423                 h = sh;
29424             }
29425
29426             if(this.preserveRatio){
29427                 switch(pos){
29428                     case "southeast":
29429                     case "east":
29430                         h = oh * (w/ow);
29431                         h = Math.min(Math.max(mh, h), mxh);
29432                         w = ow * (h/oh);
29433                        break;
29434                     case "south":
29435                         w = ow * (h/oh);
29436                         w = Math.min(Math.max(mw, w), mxw);
29437                         h = oh * (w/ow);
29438                         break;
29439                     case "northeast":
29440                         w = ow * (h/oh);
29441                         w = Math.min(Math.max(mw, w), mxw);
29442                         h = oh * (w/ow);
29443                     break;
29444                     case "north":
29445                         var tw = w;
29446                         w = ow * (h/oh);
29447                         w = Math.min(Math.max(mw, w), mxw);
29448                         h = oh * (w/ow);
29449                         x += (tw - w) / 2;
29450                         break;
29451                     case "southwest":
29452                         h = oh * (w/ow);
29453                         h = Math.min(Math.max(mh, h), mxh);
29454                         var tw = w;
29455                         w = ow * (h/oh);
29456                         x += tw - w;
29457                         break;
29458                     case "west":
29459                         var th = h;
29460                         h = oh * (w/ow);
29461                         h = Math.min(Math.max(mh, h), mxh);
29462                         y += (th - h) / 2;
29463                         var tw = w;
29464                         w = ow * (h/oh);
29465                         x += tw - w;
29466                        break;
29467                     case "northwest":
29468                         var tw = w;
29469                         var th = h;
29470                         h = oh * (w/ow);
29471                         h = Math.min(Math.max(mh, h), mxh);
29472                         w = ow * (h/oh);
29473                         y += th - h;
29474                         x += tw - w;
29475                        break;
29476
29477                 }
29478             }
29479             if (pos == 'hdrag') {
29480                 w = ow;
29481             }
29482             this.proxy.setBounds(x, y, w, h);
29483             if(this.dynamic){
29484                 this.resizeElement();
29485             }
29486             }catch(e){}
29487         }
29488         this.fireEvent("resizing", this, x, y, w, h, e);
29489     },
29490
29491     // private
29492     handleOver : function(){
29493         if(this.enabled){
29494             this.el.addClass("x-resizable-over");
29495         }
29496     },
29497
29498     // private
29499     handleOut : function(){
29500         if(!this.resizing){
29501             this.el.removeClass("x-resizable-over");
29502         }
29503     },
29504
29505     /**
29506      * Returns the element this component is bound to.
29507      * @return {Roo.Element}
29508      */
29509     getEl : function(){
29510         return this.el;
29511     },
29512
29513     /**
29514      * Returns the resizeChild element (or null).
29515      * @return {Roo.Element}
29516      */
29517     getResizeChild : function(){
29518         return this.resizeChild;
29519     },
29520     groupHandler : function()
29521     {
29522         
29523     },
29524     /**
29525      * Destroys this resizable. If the element was wrapped and
29526      * removeEl is not true then the element remains.
29527      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29528      */
29529     destroy : function(removeEl){
29530         this.proxy.remove();
29531         if(this.overlay){
29532             this.overlay.removeAllListeners();
29533             this.overlay.remove();
29534         }
29535         var ps = Roo.Resizable.positions;
29536         for(var k in ps){
29537             if(typeof ps[k] != "function" && this[ps[k]]){
29538                 var h = this[ps[k]];
29539                 h.el.removeAllListeners();
29540                 h.el.remove();
29541             }
29542         }
29543         if(removeEl){
29544             this.el.update("");
29545             this.el.remove();
29546         }
29547     }
29548 });
29549
29550 // private
29551 // hash to map config positions to true positions
29552 Roo.Resizable.positions = {
29553     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29554     hd: "hdrag"
29555 };
29556
29557 // private
29558 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29559     if(!this.tpl){
29560         // only initialize the template if resizable is used
29561         var tpl = Roo.DomHelper.createTemplate(
29562             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29563         );
29564         tpl.compile();
29565         Roo.Resizable.Handle.prototype.tpl = tpl;
29566     }
29567     this.position = pos;
29568     this.rz = rz;
29569     // show north drag fro topdra
29570     var handlepos = pos == 'hdrag' ? 'north' : pos;
29571     
29572     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29573     if (pos == 'hdrag') {
29574         this.el.setStyle('cursor', 'pointer');
29575     }
29576     this.el.unselectable();
29577     if(transparent){
29578         this.el.setOpacity(0);
29579     }
29580     this.el.on("mousedown", this.onMouseDown, this);
29581     if(!disableTrackOver){
29582         this.el.on("mouseover", this.onMouseOver, this);
29583         this.el.on("mouseout", this.onMouseOut, this);
29584     }
29585 };
29586
29587 // private
29588 Roo.Resizable.Handle.prototype = {
29589     afterResize : function(rz){
29590         Roo.log('after?');
29591         // do nothing
29592     },
29593     // private
29594     onMouseDown : function(e){
29595         this.rz.onMouseDown(this, e);
29596     },
29597     // private
29598     onMouseOver : function(e){
29599         this.rz.handleOver(this, e);
29600     },
29601     // private
29602     onMouseOut : function(e){
29603         this.rz.handleOut(this, e);
29604     }
29605 };/*
29606  * Based on:
29607  * Ext JS Library 1.1.1
29608  * Copyright(c) 2006-2007, Ext JS, LLC.
29609  *
29610  * Originally Released Under LGPL - original licence link has changed is not relivant.
29611  *
29612  * Fork - LGPL
29613  * <script type="text/javascript">
29614  */
29615
29616 /**
29617  * @class Roo.Editor
29618  * @extends Roo.Component
29619  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29620  * @constructor
29621  * Create a new Editor
29622  * @param {Roo.form.Field} field The Field object (or descendant)
29623  * @param {Object} config The config object
29624  */
29625 Roo.Editor = function(field, config){
29626     Roo.Editor.superclass.constructor.call(this, config);
29627     this.field = field;
29628     this.addEvents({
29629         /**
29630              * @event beforestartedit
29631              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29632              * false from the handler of this event.
29633              * @param {Editor} this
29634              * @param {Roo.Element} boundEl The underlying element bound to this editor
29635              * @param {Mixed} value The field value being set
29636              */
29637         "beforestartedit" : true,
29638         /**
29639              * @event startedit
29640              * Fires when this editor is displayed
29641              * @param {Roo.Element} boundEl The underlying element bound to this editor
29642              * @param {Mixed} value The starting field value
29643              */
29644         "startedit" : true,
29645         /**
29646              * @event beforecomplete
29647              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29648              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29649              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29650              * event will not fire since no edit actually occurred.
29651              * @param {Editor} this
29652              * @param {Mixed} value The current field value
29653              * @param {Mixed} startValue The original field value
29654              */
29655         "beforecomplete" : true,
29656         /**
29657              * @event complete
29658              * Fires after editing is complete and any changed value has been written to the underlying field.
29659              * @param {Editor} this
29660              * @param {Mixed} value The current field value
29661              * @param {Mixed} startValue The original field value
29662              */
29663         "complete" : true,
29664         /**
29665          * @event specialkey
29666          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29667          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29668          * @param {Roo.form.Field} this
29669          * @param {Roo.EventObject} e The event object
29670          */
29671         "specialkey" : true
29672     });
29673 };
29674
29675 Roo.extend(Roo.Editor, Roo.Component, {
29676     /**
29677      * @cfg {Boolean/String} autosize
29678      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29679      * or "height" to adopt the height only (defaults to false)
29680      */
29681     /**
29682      * @cfg {Boolean} revertInvalid
29683      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29684      * validation fails (defaults to true)
29685      */
29686     /**
29687      * @cfg {Boolean} ignoreNoChange
29688      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29689      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29690      * will never be ignored.
29691      */
29692     /**
29693      * @cfg {Boolean} hideEl
29694      * False to keep the bound element visible while the editor is displayed (defaults to true)
29695      */
29696     /**
29697      * @cfg {Mixed} value
29698      * The data value of the underlying field (defaults to "")
29699      */
29700     value : "",
29701     /**
29702      * @cfg {String} alignment
29703      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29704      */
29705     alignment: "c-c?",
29706     /**
29707      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29708      * for bottom-right shadow (defaults to "frame")
29709      */
29710     shadow : "frame",
29711     /**
29712      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29713      */
29714     constrain : false,
29715     /**
29716      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29717      */
29718     completeOnEnter : false,
29719     /**
29720      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29721      */
29722     cancelOnEsc : false,
29723     /**
29724      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29725      */
29726     updateEl : false,
29727
29728     // private
29729     onRender : function(ct, position){
29730         this.el = new Roo.Layer({
29731             shadow: this.shadow,
29732             cls: "x-editor",
29733             parentEl : ct,
29734             shim : this.shim,
29735             shadowOffset:4,
29736             id: this.id,
29737             constrain: this.constrain
29738         });
29739         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29740         if(this.field.msgTarget != 'title'){
29741             this.field.msgTarget = 'qtip';
29742         }
29743         this.field.render(this.el);
29744         if(Roo.isGecko){
29745             this.field.el.dom.setAttribute('autocomplete', 'off');
29746         }
29747         this.field.on("specialkey", this.onSpecialKey, this);
29748         if(this.swallowKeys){
29749             this.field.el.swallowEvent(['keydown','keypress']);
29750         }
29751         this.field.show();
29752         this.field.on("blur", this.onBlur, this);
29753         if(this.field.grow){
29754             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29755         }
29756     },
29757
29758     onSpecialKey : function(field, e)
29759     {
29760         //Roo.log('editor onSpecialKey');
29761         if(this.completeOnEnter && e.getKey() == e.ENTER){
29762             e.stopEvent();
29763             this.completeEdit();
29764             return;
29765         }
29766         // do not fire special key otherwise it might hide close the editor...
29767         if(e.getKey() == e.ENTER){    
29768             return;
29769         }
29770         if(this.cancelOnEsc && e.getKey() == e.ESC){
29771             this.cancelEdit();
29772             return;
29773         } 
29774         this.fireEvent('specialkey', field, e);
29775     
29776     },
29777
29778     /**
29779      * Starts the editing process and shows the editor.
29780      * @param {String/HTMLElement/Element} el The element to edit
29781      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29782       * to the innerHTML of el.
29783      */
29784     startEdit : function(el, value){
29785         if(this.editing){
29786             this.completeEdit();
29787         }
29788         this.boundEl = Roo.get(el);
29789         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29790         if(!this.rendered){
29791             this.render(this.parentEl || document.body);
29792         }
29793         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29794             return;
29795         }
29796         this.startValue = v;
29797         this.field.setValue(v);
29798         if(this.autoSize){
29799             var sz = this.boundEl.getSize();
29800             switch(this.autoSize){
29801                 case "width":
29802                 this.setSize(sz.width,  "");
29803                 break;
29804                 case "height":
29805                 this.setSize("",  sz.height);
29806                 break;
29807                 default:
29808                 this.setSize(sz.width,  sz.height);
29809             }
29810         }
29811         this.el.alignTo(this.boundEl, this.alignment);
29812         this.editing = true;
29813         if(Roo.QuickTips){
29814             Roo.QuickTips.disable();
29815         }
29816         this.show();
29817     },
29818
29819     /**
29820      * Sets the height and width of this editor.
29821      * @param {Number} width The new width
29822      * @param {Number} height The new height
29823      */
29824     setSize : function(w, h){
29825         this.field.setSize(w, h);
29826         if(this.el){
29827             this.el.sync();
29828         }
29829     },
29830
29831     /**
29832      * Realigns the editor to the bound field based on the current alignment config value.
29833      */
29834     realign : function(){
29835         this.el.alignTo(this.boundEl, this.alignment);
29836     },
29837
29838     /**
29839      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29840      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29841      */
29842     completeEdit : function(remainVisible){
29843         if(!this.editing){
29844             return;
29845         }
29846         var v = this.getValue();
29847         if(this.revertInvalid !== false && !this.field.isValid()){
29848             v = this.startValue;
29849             this.cancelEdit(true);
29850         }
29851         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29852             this.editing = false;
29853             this.hide();
29854             return;
29855         }
29856         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29857             this.editing = false;
29858             if(this.updateEl && this.boundEl){
29859                 this.boundEl.update(v);
29860             }
29861             if(remainVisible !== true){
29862                 this.hide();
29863             }
29864             this.fireEvent("complete", this, v, this.startValue);
29865         }
29866     },
29867
29868     // private
29869     onShow : function(){
29870         this.el.show();
29871         if(this.hideEl !== false){
29872             this.boundEl.hide();
29873         }
29874         this.field.show();
29875         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29876             this.fixIEFocus = true;
29877             this.deferredFocus.defer(50, this);
29878         }else{
29879             this.field.focus();
29880         }
29881         this.fireEvent("startedit", this.boundEl, this.startValue);
29882     },
29883
29884     deferredFocus : function(){
29885         if(this.editing){
29886             this.field.focus();
29887         }
29888     },
29889
29890     /**
29891      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29892      * reverted to the original starting value.
29893      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29894      * cancel (defaults to false)
29895      */
29896     cancelEdit : function(remainVisible){
29897         if(this.editing){
29898             this.setValue(this.startValue);
29899             if(remainVisible !== true){
29900                 this.hide();
29901             }
29902         }
29903     },
29904
29905     // private
29906     onBlur : function(){
29907         if(this.allowBlur !== true && this.editing){
29908             this.completeEdit();
29909         }
29910     },
29911
29912     // private
29913     onHide : function(){
29914         if(this.editing){
29915             this.completeEdit();
29916             return;
29917         }
29918         this.field.blur();
29919         if(this.field.collapse){
29920             this.field.collapse();
29921         }
29922         this.el.hide();
29923         if(this.hideEl !== false){
29924             this.boundEl.show();
29925         }
29926         if(Roo.QuickTips){
29927             Roo.QuickTips.enable();
29928         }
29929     },
29930
29931     /**
29932      * Sets the data value of the editor
29933      * @param {Mixed} value Any valid value supported by the underlying field
29934      */
29935     setValue : function(v){
29936         this.field.setValue(v);
29937     },
29938
29939     /**
29940      * Gets the data value of the editor
29941      * @return {Mixed} The data value
29942      */
29943     getValue : function(){
29944         return this.field.getValue();
29945     }
29946 });/*
29947  * Based on:
29948  * Ext JS Library 1.1.1
29949  * Copyright(c) 2006-2007, Ext JS, LLC.
29950  *
29951  * Originally Released Under LGPL - original licence link has changed is not relivant.
29952  *
29953  * Fork - LGPL
29954  * <script type="text/javascript">
29955  */
29956  
29957 /**
29958  * @class Roo.BasicDialog
29959  * @extends Roo.util.Observable
29960  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29961  * <pre><code>
29962 var dlg = new Roo.BasicDialog("my-dlg", {
29963     height: 200,
29964     width: 300,
29965     minHeight: 100,
29966     minWidth: 150,
29967     modal: true,
29968     proxyDrag: true,
29969     shadow: true
29970 });
29971 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29972 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29973 dlg.addButton('Cancel', dlg.hide, dlg);
29974 dlg.show();
29975 </code></pre>
29976   <b>A Dialog should always be a direct child of the body element.</b>
29977  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29978  * @cfg {String} title Default text to display in the title bar (defaults to null)
29979  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29980  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29981  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29982  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29983  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29984  * (defaults to null with no animation)
29985  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29986  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29987  * property for valid values (defaults to 'all')
29988  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29989  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29990  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29991  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29992  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29993  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29994  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29995  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29996  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29997  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29998  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29999  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30000  * draggable = true (defaults to false)
30001  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30002  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30003  * shadow (defaults to false)
30004  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30005  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30006  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30007  * @cfg {Array} buttons Array of buttons
30008  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30009  * @constructor
30010  * Create a new BasicDialog.
30011  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30012  * @param {Object} config Configuration options
30013  */
30014 Roo.BasicDialog = function(el, config){
30015     this.el = Roo.get(el);
30016     var dh = Roo.DomHelper;
30017     if(!this.el && config && config.autoCreate){
30018         if(typeof config.autoCreate == "object"){
30019             if(!config.autoCreate.id){
30020                 config.autoCreate.id = el;
30021             }
30022             this.el = dh.append(document.body,
30023                         config.autoCreate, true);
30024         }else{
30025             this.el = dh.append(document.body,
30026                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30027         }
30028     }
30029     el = this.el;
30030     el.setDisplayed(true);
30031     el.hide = this.hideAction;
30032     this.id = el.id;
30033     el.addClass("x-dlg");
30034
30035     Roo.apply(this, config);
30036
30037     this.proxy = el.createProxy("x-dlg-proxy");
30038     this.proxy.hide = this.hideAction;
30039     this.proxy.setOpacity(.5);
30040     this.proxy.hide();
30041
30042     if(config.width){
30043         el.setWidth(config.width);
30044     }
30045     if(config.height){
30046         el.setHeight(config.height);
30047     }
30048     this.size = el.getSize();
30049     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30050         this.xy = [config.x,config.y];
30051     }else{
30052         this.xy = el.getCenterXY(true);
30053     }
30054     /** The header element @type Roo.Element */
30055     this.header = el.child("> .x-dlg-hd");
30056     /** The body element @type Roo.Element */
30057     this.body = el.child("> .x-dlg-bd");
30058     /** The footer element @type Roo.Element */
30059     this.footer = el.child("> .x-dlg-ft");
30060
30061     if(!this.header){
30062         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30063     }
30064     if(!this.body){
30065         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30066     }
30067
30068     this.header.unselectable();
30069     if(this.title){
30070         this.header.update(this.title);
30071     }
30072     // this element allows the dialog to be focused for keyboard event
30073     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30074     this.focusEl.swallowEvent("click", true);
30075
30076     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30077
30078     // wrap the body and footer for special rendering
30079     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30080     if(this.footer){
30081         this.bwrap.dom.appendChild(this.footer.dom);
30082     }
30083
30084     this.bg = this.el.createChild({
30085         tag: "div", cls:"x-dlg-bg",
30086         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30087     });
30088     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30089
30090
30091     if(this.autoScroll !== false && !this.autoTabs){
30092         this.body.setStyle("overflow", "auto");
30093     }
30094
30095     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30096
30097     if(this.closable !== false){
30098         this.el.addClass("x-dlg-closable");
30099         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30100         this.close.on("click", this.closeClick, this);
30101         this.close.addClassOnOver("x-dlg-close-over");
30102     }
30103     if(this.collapsible !== false){
30104         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30105         this.collapseBtn.on("click", this.collapseClick, this);
30106         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30107         this.header.on("dblclick", this.collapseClick, this);
30108     }
30109     if(this.resizable !== false){
30110         this.el.addClass("x-dlg-resizable");
30111         this.resizer = new Roo.Resizable(el, {
30112             minWidth: this.minWidth || 80,
30113             minHeight:this.minHeight || 80,
30114             handles: this.resizeHandles || "all",
30115             pinned: true
30116         });
30117         this.resizer.on("beforeresize", this.beforeResize, this);
30118         this.resizer.on("resize", this.onResize, this);
30119     }
30120     if(this.draggable !== false){
30121         el.addClass("x-dlg-draggable");
30122         if (!this.proxyDrag) {
30123             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30124         }
30125         else {
30126             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30127         }
30128         dd.setHandleElId(this.header.id);
30129         dd.endDrag = this.endMove.createDelegate(this);
30130         dd.startDrag = this.startMove.createDelegate(this);
30131         dd.onDrag = this.onDrag.createDelegate(this);
30132         dd.scroll = false;
30133         this.dd = dd;
30134     }
30135     if(this.modal){
30136         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30137         this.mask.enableDisplayMode("block");
30138         this.mask.hide();
30139         this.el.addClass("x-dlg-modal");
30140     }
30141     if(this.shadow){
30142         this.shadow = new Roo.Shadow({
30143             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30144             offset : this.shadowOffset
30145         });
30146     }else{
30147         this.shadowOffset = 0;
30148     }
30149     if(Roo.useShims && this.shim !== false){
30150         this.shim = this.el.createShim();
30151         this.shim.hide = this.hideAction;
30152         this.shim.hide();
30153     }else{
30154         this.shim = false;
30155     }
30156     if(this.autoTabs){
30157         this.initTabs();
30158     }
30159     if (this.buttons) { 
30160         var bts= this.buttons;
30161         this.buttons = [];
30162         Roo.each(bts, function(b) {
30163             this.addButton(b);
30164         }, this);
30165     }
30166     
30167     
30168     this.addEvents({
30169         /**
30170          * @event keydown
30171          * Fires when a key is pressed
30172          * @param {Roo.BasicDialog} this
30173          * @param {Roo.EventObject} e
30174          */
30175         "keydown" : true,
30176         /**
30177          * @event move
30178          * Fires when this dialog is moved by the user.
30179          * @param {Roo.BasicDialog} this
30180          * @param {Number} x The new page X
30181          * @param {Number} y The new page Y
30182          */
30183         "move" : true,
30184         /**
30185          * @event resize
30186          * Fires when this dialog is resized by the user.
30187          * @param {Roo.BasicDialog} this
30188          * @param {Number} width The new width
30189          * @param {Number} height The new height
30190          */
30191         "resize" : true,
30192         /**
30193          * @event beforehide
30194          * Fires before this dialog is hidden.
30195          * @param {Roo.BasicDialog} this
30196          */
30197         "beforehide" : true,
30198         /**
30199          * @event hide
30200          * Fires when this dialog is hidden.
30201          * @param {Roo.BasicDialog} this
30202          */
30203         "hide" : true,
30204         /**
30205          * @event beforeshow
30206          * Fires before this dialog is shown.
30207          * @param {Roo.BasicDialog} this
30208          */
30209         "beforeshow" : true,
30210         /**
30211          * @event show
30212          * Fires when this dialog is shown.
30213          * @param {Roo.BasicDialog} this
30214          */
30215         "show" : true
30216     });
30217     el.on("keydown", this.onKeyDown, this);
30218     el.on("mousedown", this.toFront, this);
30219     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30220     this.el.hide();
30221     Roo.DialogManager.register(this);
30222     Roo.BasicDialog.superclass.constructor.call(this);
30223 };
30224
30225 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30226     shadowOffset: Roo.isIE ? 6 : 5,
30227     minHeight: 80,
30228     minWidth: 200,
30229     minButtonWidth: 75,
30230     defaultButton: null,
30231     buttonAlign: "right",
30232     tabTag: 'div',
30233     firstShow: true,
30234
30235     /**
30236      * Sets the dialog title text
30237      * @param {String} text The title text to display
30238      * @return {Roo.BasicDialog} this
30239      */
30240     setTitle : function(text){
30241         this.header.update(text);
30242         return this;
30243     },
30244
30245     // private
30246     closeClick : function(){
30247         this.hide();
30248     },
30249
30250     // private
30251     collapseClick : function(){
30252         this[this.collapsed ? "expand" : "collapse"]();
30253     },
30254
30255     /**
30256      * Collapses the dialog to its minimized state (only the title bar is visible).
30257      * Equivalent to the user clicking the collapse dialog button.
30258      */
30259     collapse : function(){
30260         if(!this.collapsed){
30261             this.collapsed = true;
30262             this.el.addClass("x-dlg-collapsed");
30263             this.restoreHeight = this.el.getHeight();
30264             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30265         }
30266     },
30267
30268     /**
30269      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30270      * clicking the expand dialog button.
30271      */
30272     expand : function(){
30273         if(this.collapsed){
30274             this.collapsed = false;
30275             this.el.removeClass("x-dlg-collapsed");
30276             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30277         }
30278     },
30279
30280     /**
30281      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30282      * @return {Roo.TabPanel} The tabs component
30283      */
30284     initTabs : function(){
30285         var tabs = this.getTabs();
30286         while(tabs.getTab(0)){
30287             tabs.removeTab(0);
30288         }
30289         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30290             var dom = el.dom;
30291             tabs.addTab(Roo.id(dom), dom.title);
30292             dom.title = "";
30293         });
30294         tabs.activate(0);
30295         return tabs;
30296     },
30297
30298     // private
30299     beforeResize : function(){
30300         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30301     },
30302
30303     // private
30304     onResize : function(){
30305         this.refreshSize();
30306         this.syncBodyHeight();
30307         this.adjustAssets();
30308         this.focus();
30309         this.fireEvent("resize", this, this.size.width, this.size.height);
30310     },
30311
30312     // private
30313     onKeyDown : function(e){
30314         if(this.isVisible()){
30315             this.fireEvent("keydown", this, e);
30316         }
30317     },
30318
30319     /**
30320      * Resizes the dialog.
30321      * @param {Number} width
30322      * @param {Number} height
30323      * @return {Roo.BasicDialog} this
30324      */
30325     resizeTo : function(width, height){
30326         this.el.setSize(width, height);
30327         this.size = {width: width, height: height};
30328         this.syncBodyHeight();
30329         if(this.fixedcenter){
30330             this.center();
30331         }
30332         if(this.isVisible()){
30333             this.constrainXY();
30334             this.adjustAssets();
30335         }
30336         this.fireEvent("resize", this, width, height);
30337         return this;
30338     },
30339
30340
30341     /**
30342      * Resizes the dialog to fit the specified content size.
30343      * @param {Number} width
30344      * @param {Number} height
30345      * @return {Roo.BasicDialog} this
30346      */
30347     setContentSize : function(w, h){
30348         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30349         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30350         //if(!this.el.isBorderBox()){
30351             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30352             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30353         //}
30354         if(this.tabs){
30355             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30356             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30357         }
30358         this.resizeTo(w, h);
30359         return this;
30360     },
30361
30362     /**
30363      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30364      * executed in response to a particular key being pressed while the dialog is active.
30365      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30366      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30367      * @param {Function} fn The function to call
30368      * @param {Object} scope (optional) The scope of the function
30369      * @return {Roo.BasicDialog} this
30370      */
30371     addKeyListener : function(key, fn, scope){
30372         var keyCode, shift, ctrl, alt;
30373         if(typeof key == "object" && !(key instanceof Array)){
30374             keyCode = key["key"];
30375             shift = key["shift"];
30376             ctrl = key["ctrl"];
30377             alt = key["alt"];
30378         }else{
30379             keyCode = key;
30380         }
30381         var handler = function(dlg, e){
30382             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30383                 var k = e.getKey();
30384                 if(keyCode instanceof Array){
30385                     for(var i = 0, len = keyCode.length; i < len; i++){
30386                         if(keyCode[i] == k){
30387                           fn.call(scope || window, dlg, k, e);
30388                           return;
30389                         }
30390                     }
30391                 }else{
30392                     if(k == keyCode){
30393                         fn.call(scope || window, dlg, k, e);
30394                     }
30395                 }
30396             }
30397         };
30398         this.on("keydown", handler);
30399         return this;
30400     },
30401
30402     /**
30403      * Returns the TabPanel component (creates it if it doesn't exist).
30404      * Note: If you wish to simply check for the existence of tabs without creating them,
30405      * check for a null 'tabs' property.
30406      * @return {Roo.TabPanel} The tabs component
30407      */
30408     getTabs : function(){
30409         if(!this.tabs){
30410             this.el.addClass("x-dlg-auto-tabs");
30411             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30412             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30413         }
30414         return this.tabs;
30415     },
30416
30417     /**
30418      * Adds a button to the footer section of the dialog.
30419      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30420      * object or a valid Roo.DomHelper element config
30421      * @param {Function} handler The function called when the button is clicked
30422      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30423      * @return {Roo.Button} The new button
30424      */
30425     addButton : function(config, handler, scope){
30426         var dh = Roo.DomHelper;
30427         if(!this.footer){
30428             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30429         }
30430         if(!this.btnContainer){
30431             var tb = this.footer.createChild({
30432
30433                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30434                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30435             }, null, true);
30436             this.btnContainer = tb.firstChild.firstChild.firstChild;
30437         }
30438         var bconfig = {
30439             handler: handler,
30440             scope: scope,
30441             minWidth: this.minButtonWidth,
30442             hideParent:true
30443         };
30444         if(typeof config == "string"){
30445             bconfig.text = config;
30446         }else{
30447             if(config.tag){
30448                 bconfig.dhconfig = config;
30449             }else{
30450                 Roo.apply(bconfig, config);
30451             }
30452         }
30453         var fc = false;
30454         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30455             bconfig.position = Math.max(0, bconfig.position);
30456             fc = this.btnContainer.childNodes[bconfig.position];
30457         }
30458          
30459         var btn = new Roo.Button(
30460             fc ? 
30461                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30462                 : this.btnContainer.appendChild(document.createElement("td")),
30463             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30464             bconfig
30465         );
30466         this.syncBodyHeight();
30467         if(!this.buttons){
30468             /**
30469              * Array of all the buttons that have been added to this dialog via addButton
30470              * @type Array
30471              */
30472             this.buttons = [];
30473         }
30474         this.buttons.push(btn);
30475         return btn;
30476     },
30477
30478     /**
30479      * Sets the default button to be focused when the dialog is displayed.
30480      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30481      * @return {Roo.BasicDialog} this
30482      */
30483     setDefaultButton : function(btn){
30484         this.defaultButton = btn;
30485         return this;
30486     },
30487
30488     // private
30489     getHeaderFooterHeight : function(safe){
30490         var height = 0;
30491         if(this.header){
30492            height += this.header.getHeight();
30493         }
30494         if(this.footer){
30495            var fm = this.footer.getMargins();
30496             height += (this.footer.getHeight()+fm.top+fm.bottom);
30497         }
30498         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30499         height += this.centerBg.getPadding("tb");
30500         return height;
30501     },
30502
30503     // private
30504     syncBodyHeight : function()
30505     {
30506         var bd = this.body, // the text
30507             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30508             bw = this.bwrap;
30509         var height = this.size.height - this.getHeaderFooterHeight(false);
30510         bd.setHeight(height-bd.getMargins("tb"));
30511         var hh = this.header.getHeight();
30512         var h = this.size.height-hh;
30513         cb.setHeight(h);
30514         
30515         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30516         bw.setHeight(h-cb.getPadding("tb"));
30517         
30518         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30519         bd.setWidth(bw.getWidth(true));
30520         if(this.tabs){
30521             this.tabs.syncHeight();
30522             if(Roo.isIE){
30523                 this.tabs.el.repaint();
30524             }
30525         }
30526     },
30527
30528     /**
30529      * Restores the previous state of the dialog if Roo.state is configured.
30530      * @return {Roo.BasicDialog} this
30531      */
30532     restoreState : function(){
30533         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30534         if(box && box.width){
30535             this.xy = [box.x, box.y];
30536             this.resizeTo(box.width, box.height);
30537         }
30538         return this;
30539     },
30540
30541     // private
30542     beforeShow : function(){
30543         this.expand();
30544         if(this.fixedcenter){
30545             this.xy = this.el.getCenterXY(true);
30546         }
30547         if(this.modal){
30548             Roo.get(document.body).addClass("x-body-masked");
30549             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30550             this.mask.show();
30551         }
30552         this.constrainXY();
30553     },
30554
30555     // private
30556     animShow : function(){
30557         var b = Roo.get(this.animateTarget).getBox();
30558         this.proxy.setSize(b.width, b.height);
30559         this.proxy.setLocation(b.x, b.y);
30560         this.proxy.show();
30561         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30562                     true, .35, this.showEl.createDelegate(this));
30563     },
30564
30565     /**
30566      * Shows the dialog.
30567      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30568      * @return {Roo.BasicDialog} this
30569      */
30570     show : function(animateTarget){
30571         if (this.fireEvent("beforeshow", this) === false){
30572             return;
30573         }
30574         if(this.syncHeightBeforeShow){
30575             this.syncBodyHeight();
30576         }else if(this.firstShow){
30577             this.firstShow = false;
30578             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30579         }
30580         this.animateTarget = animateTarget || this.animateTarget;
30581         if(!this.el.isVisible()){
30582             this.beforeShow();
30583             if(this.animateTarget && Roo.get(this.animateTarget)){
30584                 this.animShow();
30585             }else{
30586                 this.showEl();
30587             }
30588         }
30589         return this;
30590     },
30591
30592     // private
30593     showEl : function(){
30594         this.proxy.hide();
30595         this.el.setXY(this.xy);
30596         this.el.show();
30597         this.adjustAssets(true);
30598         this.toFront();
30599         this.focus();
30600         // IE peekaboo bug - fix found by Dave Fenwick
30601         if(Roo.isIE){
30602             this.el.repaint();
30603         }
30604         this.fireEvent("show", this);
30605     },
30606
30607     /**
30608      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30609      * dialog itself will receive focus.
30610      */
30611     focus : function(){
30612         if(this.defaultButton){
30613             this.defaultButton.focus();
30614         }else{
30615             this.focusEl.focus();
30616         }
30617     },
30618
30619     // private
30620     constrainXY : function(){
30621         if(this.constraintoviewport !== false){
30622             if(!this.viewSize){
30623                 if(this.container){
30624                     var s = this.container.getSize();
30625                     this.viewSize = [s.width, s.height];
30626                 }else{
30627                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30628                 }
30629             }
30630             var s = Roo.get(this.container||document).getScroll();
30631
30632             var x = this.xy[0], y = this.xy[1];
30633             var w = this.size.width, h = this.size.height;
30634             var vw = this.viewSize[0], vh = this.viewSize[1];
30635             // only move it if it needs it
30636             var moved = false;
30637             // first validate right/bottom
30638             if(x + w > vw+s.left){
30639                 x = vw - w;
30640                 moved = true;
30641             }
30642             if(y + h > vh+s.top){
30643                 y = vh - h;
30644                 moved = true;
30645             }
30646             // then make sure top/left isn't negative
30647             if(x < s.left){
30648                 x = s.left;
30649                 moved = true;
30650             }
30651             if(y < s.top){
30652                 y = s.top;
30653                 moved = true;
30654             }
30655             if(moved){
30656                 // cache xy
30657                 this.xy = [x, y];
30658                 if(this.isVisible()){
30659                     this.el.setLocation(x, y);
30660                     this.adjustAssets();
30661                 }
30662             }
30663         }
30664     },
30665
30666     // private
30667     onDrag : function(){
30668         if(!this.proxyDrag){
30669             this.xy = this.el.getXY();
30670             this.adjustAssets();
30671         }
30672     },
30673
30674     // private
30675     adjustAssets : function(doShow){
30676         var x = this.xy[0], y = this.xy[1];
30677         var w = this.size.width, h = this.size.height;
30678         if(doShow === true){
30679             if(this.shadow){
30680                 this.shadow.show(this.el);
30681             }
30682             if(this.shim){
30683                 this.shim.show();
30684             }
30685         }
30686         if(this.shadow && this.shadow.isVisible()){
30687             this.shadow.show(this.el);
30688         }
30689         if(this.shim && this.shim.isVisible()){
30690             this.shim.setBounds(x, y, w, h);
30691         }
30692     },
30693
30694     // private
30695     adjustViewport : function(w, h){
30696         if(!w || !h){
30697             w = Roo.lib.Dom.getViewWidth();
30698             h = Roo.lib.Dom.getViewHeight();
30699         }
30700         // cache the size
30701         this.viewSize = [w, h];
30702         if(this.modal && this.mask.isVisible()){
30703             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30704             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30705         }
30706         if(this.isVisible()){
30707             this.constrainXY();
30708         }
30709     },
30710
30711     /**
30712      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30713      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30714      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30715      */
30716     destroy : function(removeEl){
30717         if(this.isVisible()){
30718             this.animateTarget = null;
30719             this.hide();
30720         }
30721         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30722         if(this.tabs){
30723             this.tabs.destroy(removeEl);
30724         }
30725         Roo.destroy(
30726              this.shim,
30727              this.proxy,
30728              this.resizer,
30729              this.close,
30730              this.mask
30731         );
30732         if(this.dd){
30733             this.dd.unreg();
30734         }
30735         if(this.buttons){
30736            for(var i = 0, len = this.buttons.length; i < len; i++){
30737                this.buttons[i].destroy();
30738            }
30739         }
30740         this.el.removeAllListeners();
30741         if(removeEl === true){
30742             this.el.update("");
30743             this.el.remove();
30744         }
30745         Roo.DialogManager.unregister(this);
30746     },
30747
30748     // private
30749     startMove : function(){
30750         if(this.proxyDrag){
30751             this.proxy.show();
30752         }
30753         if(this.constraintoviewport !== false){
30754             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30755         }
30756     },
30757
30758     // private
30759     endMove : function(){
30760         if(!this.proxyDrag){
30761             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30762         }else{
30763             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30764             this.proxy.hide();
30765         }
30766         this.refreshSize();
30767         this.adjustAssets();
30768         this.focus();
30769         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30770     },
30771
30772     /**
30773      * Brings this dialog to the front of any other visible dialogs
30774      * @return {Roo.BasicDialog} this
30775      */
30776     toFront : function(){
30777         Roo.DialogManager.bringToFront(this);
30778         return this;
30779     },
30780
30781     /**
30782      * Sends this dialog to the back (under) of any other visible dialogs
30783      * @return {Roo.BasicDialog} this
30784      */
30785     toBack : function(){
30786         Roo.DialogManager.sendToBack(this);
30787         return this;
30788     },
30789
30790     /**
30791      * Centers this dialog in the viewport
30792      * @return {Roo.BasicDialog} this
30793      */
30794     center : function(){
30795         var xy = this.el.getCenterXY(true);
30796         this.moveTo(xy[0], xy[1]);
30797         return this;
30798     },
30799
30800     /**
30801      * Moves the dialog's top-left corner to the specified point
30802      * @param {Number} x
30803      * @param {Number} y
30804      * @return {Roo.BasicDialog} this
30805      */
30806     moveTo : function(x, y){
30807         this.xy = [x,y];
30808         if(this.isVisible()){
30809             this.el.setXY(this.xy);
30810             this.adjustAssets();
30811         }
30812         return this;
30813     },
30814
30815     /**
30816      * Aligns the dialog to the specified element
30817      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30818      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30819      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30820      * @return {Roo.BasicDialog} this
30821      */
30822     alignTo : function(element, position, offsets){
30823         this.xy = this.el.getAlignToXY(element, position, offsets);
30824         if(this.isVisible()){
30825             this.el.setXY(this.xy);
30826             this.adjustAssets();
30827         }
30828         return this;
30829     },
30830
30831     /**
30832      * Anchors an element to another element and realigns it when the window is resized.
30833      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30834      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30835      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30836      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30837      * is a number, it is used as the buffer delay (defaults to 50ms).
30838      * @return {Roo.BasicDialog} this
30839      */
30840     anchorTo : function(el, alignment, offsets, monitorScroll){
30841         var action = function(){
30842             this.alignTo(el, alignment, offsets);
30843         };
30844         Roo.EventManager.onWindowResize(action, this);
30845         var tm = typeof monitorScroll;
30846         if(tm != 'undefined'){
30847             Roo.EventManager.on(window, 'scroll', action, this,
30848                 {buffer: tm == 'number' ? monitorScroll : 50});
30849         }
30850         action.call(this);
30851         return this;
30852     },
30853
30854     /**
30855      * Returns true if the dialog is visible
30856      * @return {Boolean}
30857      */
30858     isVisible : function(){
30859         return this.el.isVisible();
30860     },
30861
30862     // private
30863     animHide : function(callback){
30864         var b = Roo.get(this.animateTarget).getBox();
30865         this.proxy.show();
30866         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30867         this.el.hide();
30868         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30869                     this.hideEl.createDelegate(this, [callback]));
30870     },
30871
30872     /**
30873      * Hides the dialog.
30874      * @param {Function} callback (optional) Function to call when the dialog is hidden
30875      * @return {Roo.BasicDialog} this
30876      */
30877     hide : function(callback){
30878         if (this.fireEvent("beforehide", this) === false){
30879             return;
30880         }
30881         if(this.shadow){
30882             this.shadow.hide();
30883         }
30884         if(this.shim) {
30885           this.shim.hide();
30886         }
30887         // sometimes animateTarget seems to get set.. causing problems...
30888         // this just double checks..
30889         if(this.animateTarget && Roo.get(this.animateTarget)) {
30890            this.animHide(callback);
30891         }else{
30892             this.el.hide();
30893             this.hideEl(callback);
30894         }
30895         return this;
30896     },
30897
30898     // private
30899     hideEl : function(callback){
30900         this.proxy.hide();
30901         if(this.modal){
30902             this.mask.hide();
30903             Roo.get(document.body).removeClass("x-body-masked");
30904         }
30905         this.fireEvent("hide", this);
30906         if(typeof callback == "function"){
30907             callback();
30908         }
30909     },
30910
30911     // private
30912     hideAction : function(){
30913         this.setLeft("-10000px");
30914         this.setTop("-10000px");
30915         this.setStyle("visibility", "hidden");
30916     },
30917
30918     // private
30919     refreshSize : function(){
30920         this.size = this.el.getSize();
30921         this.xy = this.el.getXY();
30922         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30923     },
30924
30925     // private
30926     // z-index is managed by the DialogManager and may be overwritten at any time
30927     setZIndex : function(index){
30928         if(this.modal){
30929             this.mask.setStyle("z-index", index);
30930         }
30931         if(this.shim){
30932             this.shim.setStyle("z-index", ++index);
30933         }
30934         if(this.shadow){
30935             this.shadow.setZIndex(++index);
30936         }
30937         this.el.setStyle("z-index", ++index);
30938         if(this.proxy){
30939             this.proxy.setStyle("z-index", ++index);
30940         }
30941         if(this.resizer){
30942             this.resizer.proxy.setStyle("z-index", ++index);
30943         }
30944
30945         this.lastZIndex = index;
30946     },
30947
30948     /**
30949      * Returns the element for this dialog
30950      * @return {Roo.Element} The underlying dialog Element
30951      */
30952     getEl : function(){
30953         return this.el;
30954     }
30955 });
30956
30957 /**
30958  * @class Roo.DialogManager
30959  * Provides global access to BasicDialogs that have been created and
30960  * support for z-indexing (layering) multiple open dialogs.
30961  */
30962 Roo.DialogManager = function(){
30963     var list = {};
30964     var accessList = [];
30965     var front = null;
30966
30967     // private
30968     var sortDialogs = function(d1, d2){
30969         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30970     };
30971
30972     // private
30973     var orderDialogs = function(){
30974         accessList.sort(sortDialogs);
30975         var seed = Roo.DialogManager.zseed;
30976         for(var i = 0, len = accessList.length; i < len; i++){
30977             var dlg = accessList[i];
30978             if(dlg){
30979                 dlg.setZIndex(seed + (i*10));
30980             }
30981         }
30982     };
30983
30984     return {
30985         /**
30986          * The starting z-index for BasicDialogs (defaults to 9000)
30987          * @type Number The z-index value
30988          */
30989         zseed : 9000,
30990
30991         // private
30992         register : function(dlg){
30993             list[dlg.id] = dlg;
30994             accessList.push(dlg);
30995         },
30996
30997         // private
30998         unregister : function(dlg){
30999             delete list[dlg.id];
31000             var i=0;
31001             var len=0;
31002             if(!accessList.indexOf){
31003                 for(  i = 0, len = accessList.length; i < len; i++){
31004                     if(accessList[i] == dlg){
31005                         accessList.splice(i, 1);
31006                         return;
31007                     }
31008                 }
31009             }else{
31010                  i = accessList.indexOf(dlg);
31011                 if(i != -1){
31012                     accessList.splice(i, 1);
31013                 }
31014             }
31015         },
31016
31017         /**
31018          * Gets a registered dialog by id
31019          * @param {String/Object} id The id of the dialog or a dialog
31020          * @return {Roo.BasicDialog} this
31021          */
31022         get : function(id){
31023             return typeof id == "object" ? id : list[id];
31024         },
31025
31026         /**
31027          * Brings the specified dialog to the front
31028          * @param {String/Object} dlg The id of the dialog or a dialog
31029          * @return {Roo.BasicDialog} this
31030          */
31031         bringToFront : function(dlg){
31032             dlg = this.get(dlg);
31033             if(dlg != front){
31034                 front = dlg;
31035                 dlg._lastAccess = new Date().getTime();
31036                 orderDialogs();
31037             }
31038             return dlg;
31039         },
31040
31041         /**
31042          * Sends the specified dialog to the back
31043          * @param {String/Object} dlg The id of the dialog or a dialog
31044          * @return {Roo.BasicDialog} this
31045          */
31046         sendToBack : function(dlg){
31047             dlg = this.get(dlg);
31048             dlg._lastAccess = -(new Date().getTime());
31049             orderDialogs();
31050             return dlg;
31051         },
31052
31053         /**
31054          * Hides all dialogs
31055          */
31056         hideAll : function(){
31057             for(var id in list){
31058                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31059                     list[id].hide();
31060                 }
31061             }
31062         }
31063     };
31064 }();
31065
31066 /**
31067  * @class Roo.LayoutDialog
31068  * @extends Roo.BasicDialog
31069  * Dialog which provides adjustments for working with a layout in a Dialog.
31070  * Add your necessary layout config options to the dialog's config.<br>
31071  * Example usage (including a nested layout):
31072  * <pre><code>
31073 if(!dialog){
31074     dialog = new Roo.LayoutDialog("download-dlg", {
31075         modal: true,
31076         width:600,
31077         height:450,
31078         shadow:true,
31079         minWidth:500,
31080         minHeight:350,
31081         autoTabs:true,
31082         proxyDrag:true,
31083         // layout config merges with the dialog config
31084         center:{
31085             tabPosition: "top",
31086             alwaysShowTabs: true
31087         }
31088     });
31089     dialog.addKeyListener(27, dialog.hide, dialog);
31090     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31091     dialog.addButton("Build It!", this.getDownload, this);
31092
31093     // we can even add nested layouts
31094     var innerLayout = new Roo.BorderLayout("dl-inner", {
31095         east: {
31096             initialSize: 200,
31097             autoScroll:true,
31098             split:true
31099         },
31100         center: {
31101             autoScroll:true
31102         }
31103     });
31104     innerLayout.beginUpdate();
31105     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31106     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31107     innerLayout.endUpdate(true);
31108
31109     var layout = dialog.getLayout();
31110     layout.beginUpdate();
31111     layout.add("center", new Roo.ContentPanel("standard-panel",
31112                         {title: "Download the Source", fitToFrame:true}));
31113     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31114                {title: "Build your own roo.js"}));
31115     layout.getRegion("center").showPanel(sp);
31116     layout.endUpdate();
31117 }
31118 </code></pre>
31119     * @constructor
31120     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31121     * @param {Object} config configuration options
31122   */
31123 Roo.LayoutDialog = function(el, cfg){
31124     
31125     var config=  cfg;
31126     if (typeof(cfg) == 'undefined') {
31127         config = Roo.apply({}, el);
31128         // not sure why we use documentElement here.. - it should always be body.
31129         // IE7 borks horribly if we use documentElement.
31130         // webkit also does not like documentElement - it creates a body element...
31131         el = Roo.get( document.body || document.documentElement ).createChild();
31132         //config.autoCreate = true;
31133     }
31134     
31135     
31136     config.autoTabs = false;
31137     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31138     this.body.setStyle({overflow:"hidden", position:"relative"});
31139     this.layout = new Roo.BorderLayout(this.body.dom, config);
31140     this.layout.monitorWindowResize = false;
31141     this.el.addClass("x-dlg-auto-layout");
31142     // fix case when center region overwrites center function
31143     this.center = Roo.BasicDialog.prototype.center;
31144     this.on("show", this.layout.layout, this.layout, true);
31145     if (config.items) {
31146         var xitems = config.items;
31147         delete config.items;
31148         Roo.each(xitems, this.addxtype, this);
31149     }
31150     
31151     
31152 };
31153 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31154     /**
31155      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31156      * @deprecated
31157      */
31158     endUpdate : function(){
31159         this.layout.endUpdate();
31160     },
31161
31162     /**
31163      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31164      *  @deprecated
31165      */
31166     beginUpdate : function(){
31167         this.layout.beginUpdate();
31168     },
31169
31170     /**
31171      * Get the BorderLayout for this dialog
31172      * @return {Roo.BorderLayout}
31173      */
31174     getLayout : function(){
31175         return this.layout;
31176     },
31177
31178     showEl : function(){
31179         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31180         if(Roo.isIE7){
31181             this.layout.layout();
31182         }
31183     },
31184
31185     // private
31186     // Use the syncHeightBeforeShow config option to control this automatically
31187     syncBodyHeight : function(){
31188         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31189         if(this.layout){this.layout.layout();}
31190     },
31191     
31192       /**
31193      * Add an xtype element (actually adds to the layout.)
31194      * @return {Object} xdata xtype object data.
31195      */
31196     
31197     addxtype : function(c) {
31198         return this.layout.addxtype(c);
31199     }
31200 });/*
31201  * Based on:
31202  * Ext JS Library 1.1.1
31203  * Copyright(c) 2006-2007, Ext JS, LLC.
31204  *
31205  * Originally Released Under LGPL - original licence link has changed is not relivant.
31206  *
31207  * Fork - LGPL
31208  * <script type="text/javascript">
31209  */
31210  
31211 /**
31212  * @class Roo.MessageBox
31213  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31214  * Example usage:
31215  *<pre><code>
31216 // Basic alert:
31217 Roo.Msg.alert('Status', 'Changes saved successfully.');
31218
31219 // Prompt for user data:
31220 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31221     if (btn == 'ok'){
31222         // process text value...
31223     }
31224 });
31225
31226 // Show a dialog using config options:
31227 Roo.Msg.show({
31228    title:'Save Changes?',
31229    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31230    buttons: Roo.Msg.YESNOCANCEL,
31231    fn: processResult,
31232    animEl: 'elId'
31233 });
31234 </code></pre>
31235  * @singleton
31236  */
31237 Roo.MessageBox = function(){
31238     var dlg, opt, mask, waitTimer;
31239     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31240     var buttons, activeTextEl, bwidth;
31241
31242     // private
31243     var handleButton = function(button){
31244         dlg.hide();
31245         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31246     };
31247
31248     // private
31249     var handleHide = function(){
31250         if(opt && opt.cls){
31251             dlg.el.removeClass(opt.cls);
31252         }
31253         if(waitTimer){
31254             Roo.TaskMgr.stop(waitTimer);
31255             waitTimer = null;
31256         }
31257     };
31258
31259     // private
31260     var updateButtons = function(b){
31261         var width = 0;
31262         if(!b){
31263             buttons["ok"].hide();
31264             buttons["cancel"].hide();
31265             buttons["yes"].hide();
31266             buttons["no"].hide();
31267             dlg.footer.dom.style.display = 'none';
31268             return width;
31269         }
31270         dlg.footer.dom.style.display = '';
31271         for(var k in buttons){
31272             if(typeof buttons[k] != "function"){
31273                 if(b[k]){
31274                     buttons[k].show();
31275                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31276                     width += buttons[k].el.getWidth()+15;
31277                 }else{
31278                     buttons[k].hide();
31279                 }
31280             }
31281         }
31282         return width;
31283     };
31284
31285     // private
31286     var handleEsc = function(d, k, e){
31287         if(opt && opt.closable !== false){
31288             dlg.hide();
31289         }
31290         if(e){
31291             e.stopEvent();
31292         }
31293     };
31294
31295     return {
31296         /**
31297          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31298          * @return {Roo.BasicDialog} The BasicDialog element
31299          */
31300         getDialog : function(){
31301            if(!dlg){
31302                 dlg = new Roo.BasicDialog("x-msg-box", {
31303                     autoCreate : true,
31304                     shadow: true,
31305                     draggable: true,
31306                     resizable:false,
31307                     constraintoviewport:false,
31308                     fixedcenter:true,
31309                     collapsible : false,
31310                     shim:true,
31311                     modal: true,
31312                     width:400, height:100,
31313                     buttonAlign:"center",
31314                     closeClick : function(){
31315                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31316                             handleButton("no");
31317                         }else{
31318                             handleButton("cancel");
31319                         }
31320                     }
31321                 });
31322                 dlg.on("hide", handleHide);
31323                 mask = dlg.mask;
31324                 dlg.addKeyListener(27, handleEsc);
31325                 buttons = {};
31326                 var bt = this.buttonText;
31327                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31328                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31329                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31330                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31331                 bodyEl = dlg.body.createChild({
31332
31333                     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>'
31334                 });
31335                 msgEl = bodyEl.dom.firstChild;
31336                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31337                 textboxEl.enableDisplayMode();
31338                 textboxEl.addKeyListener([10,13], function(){
31339                     if(dlg.isVisible() && opt && opt.buttons){
31340                         if(opt.buttons.ok){
31341                             handleButton("ok");
31342                         }else if(opt.buttons.yes){
31343                             handleButton("yes");
31344                         }
31345                     }
31346                 });
31347                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31348                 textareaEl.enableDisplayMode();
31349                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31350                 progressEl.enableDisplayMode();
31351                 var pf = progressEl.dom.firstChild;
31352                 if (pf) {
31353                     pp = Roo.get(pf.firstChild);
31354                     pp.setHeight(pf.offsetHeight);
31355                 }
31356                 
31357             }
31358             return dlg;
31359         },
31360
31361         /**
31362          * Updates the message box body text
31363          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31364          * the XHTML-compliant non-breaking space character '&amp;#160;')
31365          * @return {Roo.MessageBox} This message box
31366          */
31367         updateText : function(text){
31368             if(!dlg.isVisible() && !opt.width){
31369                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31370             }
31371             msgEl.innerHTML = text || '&#160;';
31372       
31373             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31374             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31375             var w = Math.max(
31376                     Math.min(opt.width || cw , this.maxWidth), 
31377                     Math.max(opt.minWidth || this.minWidth, bwidth)
31378             );
31379             if(opt.prompt){
31380                 activeTextEl.setWidth(w);
31381             }
31382             if(dlg.isVisible()){
31383                 dlg.fixedcenter = false;
31384             }
31385             // to big, make it scroll. = But as usual stupid IE does not support
31386             // !important..
31387             
31388             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31389                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31390                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31391             } else {
31392                 bodyEl.dom.style.height = '';
31393                 bodyEl.dom.style.overflowY = '';
31394             }
31395             if (cw > w) {
31396                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31397             } else {
31398                 bodyEl.dom.style.overflowX = '';
31399             }
31400             
31401             dlg.setContentSize(w, bodyEl.getHeight());
31402             if(dlg.isVisible()){
31403                 dlg.fixedcenter = true;
31404             }
31405             return this;
31406         },
31407
31408         /**
31409          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31410          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31411          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31412          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31413          * @return {Roo.MessageBox} This message box
31414          */
31415         updateProgress : function(value, text){
31416             if(text){
31417                 this.updateText(text);
31418             }
31419             if (pp) { // weird bug on my firefox - for some reason this is not defined
31420                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31421             }
31422             return this;
31423         },        
31424
31425         /**
31426          * Returns true if the message box is currently displayed
31427          * @return {Boolean} True if the message box is visible, else false
31428          */
31429         isVisible : function(){
31430             return dlg && dlg.isVisible();  
31431         },
31432
31433         /**
31434          * Hides the message box if it is displayed
31435          */
31436         hide : function(){
31437             if(this.isVisible()){
31438                 dlg.hide();
31439             }  
31440         },
31441
31442         /**
31443          * Displays a new message box, or reinitializes an existing message box, based on the config options
31444          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31445          * The following config object properties are supported:
31446          * <pre>
31447 Property    Type             Description
31448 ----------  ---------------  ------------------------------------------------------------------------------------
31449 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31450                                    closes (defaults to undefined)
31451 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31452                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31453 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31454                                    progress and wait dialogs will ignore this property and always hide the
31455                                    close button as they can only be closed programmatically.
31456 cls               String           A custom CSS class to apply to the message box element
31457 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31458                                    displayed (defaults to 75)
31459 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31460                                    function will be btn (the name of the button that was clicked, if applicable,
31461                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31462                                    Progress and wait dialogs will ignore this option since they do not respond to
31463                                    user actions and can only be closed programmatically, so any required function
31464                                    should be called by the same code after it closes the dialog.
31465 icon              String           A CSS class that provides a background image to be used as an icon for
31466                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31467 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31468 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31469 modal             Boolean          False to allow user interaction with the page while the message box is
31470                                    displayed (defaults to true)
31471 msg               String           A string that will replace the existing message box body text (defaults
31472                                    to the XHTML-compliant non-breaking space character '&#160;')
31473 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31474 progress          Boolean          True to display a progress bar (defaults to false)
31475 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31476 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31477 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31478 title             String           The title text
31479 value             String           The string value to set into the active textbox element if displayed
31480 wait              Boolean          True to display a progress bar (defaults to false)
31481 width             Number           The width of the dialog in pixels
31482 </pre>
31483          *
31484          * Example usage:
31485          * <pre><code>
31486 Roo.Msg.show({
31487    title: 'Address',
31488    msg: 'Please enter your address:',
31489    width: 300,
31490    buttons: Roo.MessageBox.OKCANCEL,
31491    multiline: true,
31492    fn: saveAddress,
31493    animEl: 'addAddressBtn'
31494 });
31495 </code></pre>
31496          * @param {Object} config Configuration options
31497          * @return {Roo.MessageBox} This message box
31498          */
31499         show : function(options)
31500         {
31501             
31502             // this causes nightmares if you show one dialog after another
31503             // especially on callbacks..
31504              
31505             if(this.isVisible()){
31506                 
31507                 this.hide();
31508                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31509                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31510                 Roo.log("New Dialog Message:" +  options.msg )
31511                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31512                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31513                 
31514             }
31515             var d = this.getDialog();
31516             opt = options;
31517             d.setTitle(opt.title || "&#160;");
31518             d.close.setDisplayed(opt.closable !== false);
31519             activeTextEl = textboxEl;
31520             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31521             if(opt.prompt){
31522                 if(opt.multiline){
31523                     textboxEl.hide();
31524                     textareaEl.show();
31525                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31526                         opt.multiline : this.defaultTextHeight);
31527                     activeTextEl = textareaEl;
31528                 }else{
31529                     textboxEl.show();
31530                     textareaEl.hide();
31531                 }
31532             }else{
31533                 textboxEl.hide();
31534                 textareaEl.hide();
31535             }
31536             progressEl.setDisplayed(opt.progress === true);
31537             this.updateProgress(0);
31538             activeTextEl.dom.value = opt.value || "";
31539             if(opt.prompt){
31540                 dlg.setDefaultButton(activeTextEl);
31541             }else{
31542                 var bs = opt.buttons;
31543                 var db = null;
31544                 if(bs && bs.ok){
31545                     db = buttons["ok"];
31546                 }else if(bs && bs.yes){
31547                     db = buttons["yes"];
31548                 }
31549                 dlg.setDefaultButton(db);
31550             }
31551             bwidth = updateButtons(opt.buttons);
31552             this.updateText(opt.msg);
31553             if(opt.cls){
31554                 d.el.addClass(opt.cls);
31555             }
31556             d.proxyDrag = opt.proxyDrag === true;
31557             d.modal = opt.modal !== false;
31558             d.mask = opt.modal !== false ? mask : false;
31559             if(!d.isVisible()){
31560                 // force it to the end of the z-index stack so it gets a cursor in FF
31561                 document.body.appendChild(dlg.el.dom);
31562                 d.animateTarget = null;
31563                 d.show(options.animEl);
31564             }
31565             return this;
31566         },
31567
31568         /**
31569          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31570          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31571          * and closing the message box when the process is complete.
31572          * @param {String} title The title bar text
31573          * @param {String} msg The message box body text
31574          * @return {Roo.MessageBox} This message box
31575          */
31576         progress : function(title, msg){
31577             this.show({
31578                 title : title,
31579                 msg : msg,
31580                 buttons: false,
31581                 progress:true,
31582                 closable:false,
31583                 minWidth: this.minProgressWidth,
31584                 modal : true
31585             });
31586             return this;
31587         },
31588
31589         /**
31590          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31591          * If a callback function is passed it will be called after the user clicks the button, and the
31592          * id of the button that was clicked will be passed as the only parameter to the callback
31593          * (could also be the top-right close button).
31594          * @param {String} title The title bar text
31595          * @param {String} msg The message box body text
31596          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31597          * @param {Object} scope (optional) The scope of the callback function
31598          * @return {Roo.MessageBox} This message box
31599          */
31600         alert : function(title, msg, fn, scope){
31601             this.show({
31602                 title : title,
31603                 msg : msg,
31604                 buttons: this.OK,
31605                 fn: fn,
31606                 scope : scope,
31607                 modal : true
31608             });
31609             return this;
31610         },
31611
31612         /**
31613          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31614          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31615          * You are responsible for closing the message box when the process is complete.
31616          * @param {String} msg The message box body text
31617          * @param {String} title (optional) The title bar text
31618          * @return {Roo.MessageBox} This message box
31619          */
31620         wait : function(msg, title){
31621             this.show({
31622                 title : title,
31623                 msg : msg,
31624                 buttons: false,
31625                 closable:false,
31626                 progress:true,
31627                 modal:true,
31628                 width:300,
31629                 wait:true
31630             });
31631             waitTimer = Roo.TaskMgr.start({
31632                 run: function(i){
31633                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31634                 },
31635                 interval: 1000
31636             });
31637             return this;
31638         },
31639
31640         /**
31641          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31642          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31643          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31644          * @param {String} title The title bar text
31645          * @param {String} msg The message box body text
31646          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31647          * @param {Object} scope (optional) The scope of the callback function
31648          * @return {Roo.MessageBox} This message box
31649          */
31650         confirm : function(title, msg, fn, scope){
31651             this.show({
31652                 title : title,
31653                 msg : msg,
31654                 buttons: this.YESNO,
31655                 fn: fn,
31656                 scope : scope,
31657                 modal : true
31658             });
31659             return this;
31660         },
31661
31662         /**
31663          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31664          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31665          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31666          * (could also be the top-right close button) and the text that was entered will be passed as the two
31667          * parameters to the callback.
31668          * @param {String} title The title bar text
31669          * @param {String} msg The message box body text
31670          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31671          * @param {Object} scope (optional) The scope of the callback function
31672          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31673          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31674          * @return {Roo.MessageBox} This message box
31675          */
31676         prompt : function(title, msg, fn, scope, multiline){
31677             this.show({
31678                 title : title,
31679                 msg : msg,
31680                 buttons: this.OKCANCEL,
31681                 fn: fn,
31682                 minWidth:250,
31683                 scope : scope,
31684                 prompt:true,
31685                 multiline: multiline,
31686                 modal : true
31687             });
31688             return this;
31689         },
31690
31691         /**
31692          * Button config that displays a single OK button
31693          * @type Object
31694          */
31695         OK : {ok:true},
31696         /**
31697          * Button config that displays Yes and No buttons
31698          * @type Object
31699          */
31700         YESNO : {yes:true, no:true},
31701         /**
31702          * Button config that displays OK and Cancel buttons
31703          * @type Object
31704          */
31705         OKCANCEL : {ok:true, cancel:true},
31706         /**
31707          * Button config that displays Yes, No and Cancel buttons
31708          * @type Object
31709          */
31710         YESNOCANCEL : {yes:true, no:true, cancel:true},
31711
31712         /**
31713          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31714          * @type Number
31715          */
31716         defaultTextHeight : 75,
31717         /**
31718          * The maximum width in pixels of the message box (defaults to 600)
31719          * @type Number
31720          */
31721         maxWidth : 600,
31722         /**
31723          * The minimum width in pixels of the message box (defaults to 100)
31724          * @type Number
31725          */
31726         minWidth : 100,
31727         /**
31728          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31729          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31730          * @type Number
31731          */
31732         minProgressWidth : 250,
31733         /**
31734          * An object containing the default button text strings that can be overriden for localized language support.
31735          * Supported properties are: ok, cancel, yes and no.
31736          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31737          * @type Object
31738          */
31739         buttonText : {
31740             ok : "OK",
31741             cancel : "Cancel",
31742             yes : "Yes",
31743             no : "No"
31744         }
31745     };
31746 }();
31747
31748 /**
31749  * Shorthand for {@link Roo.MessageBox}
31750  */
31751 Roo.Msg = Roo.MessageBox;/*
31752  * Based on:
31753  * Ext JS Library 1.1.1
31754  * Copyright(c) 2006-2007, Ext JS, LLC.
31755  *
31756  * Originally Released Under LGPL - original licence link has changed is not relivant.
31757  *
31758  * Fork - LGPL
31759  * <script type="text/javascript">
31760  */
31761 /**
31762  * @class Roo.QuickTips
31763  * Provides attractive and customizable tooltips for any element.
31764  * @singleton
31765  */
31766 Roo.QuickTips = function(){
31767     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31768     var ce, bd, xy, dd;
31769     var visible = false, disabled = true, inited = false;
31770     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31771     
31772     var onOver = function(e){
31773         if(disabled){
31774             return;
31775         }
31776         var t = e.getTarget();
31777         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31778             return;
31779         }
31780         if(ce && t == ce.el){
31781             clearTimeout(hideProc);
31782             return;
31783         }
31784         if(t && tagEls[t.id]){
31785             tagEls[t.id].el = t;
31786             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31787             return;
31788         }
31789         var ttp, et = Roo.fly(t);
31790         var ns = cfg.namespace;
31791         if(tm.interceptTitles && t.title){
31792             ttp = t.title;
31793             t.qtip = ttp;
31794             t.removeAttribute("title");
31795             e.preventDefault();
31796         }else{
31797             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31798         }
31799         if(ttp){
31800             showProc = show.defer(tm.showDelay, tm, [{
31801                 el: t, 
31802                 text: ttp, 
31803                 width: et.getAttributeNS(ns, cfg.width),
31804                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31805                 title: et.getAttributeNS(ns, cfg.title),
31806                     cls: et.getAttributeNS(ns, cfg.cls)
31807             }]);
31808         }
31809     };
31810     
31811     var onOut = function(e){
31812         clearTimeout(showProc);
31813         var t = e.getTarget();
31814         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31815             hideProc = setTimeout(hide, tm.hideDelay);
31816         }
31817     };
31818     
31819     var onMove = function(e){
31820         if(disabled){
31821             return;
31822         }
31823         xy = e.getXY();
31824         xy[1] += 18;
31825         if(tm.trackMouse && ce){
31826             el.setXY(xy);
31827         }
31828     };
31829     
31830     var onDown = function(e){
31831         clearTimeout(showProc);
31832         clearTimeout(hideProc);
31833         if(!e.within(el)){
31834             if(tm.hideOnClick){
31835                 hide();
31836                 tm.disable();
31837                 tm.enable.defer(100, tm);
31838             }
31839         }
31840     };
31841     
31842     var getPad = function(){
31843         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31844     };
31845
31846     var show = function(o){
31847         if(disabled){
31848             return;
31849         }
31850         clearTimeout(dismissProc);
31851         ce = o;
31852         if(removeCls){ // in case manually hidden
31853             el.removeClass(removeCls);
31854             removeCls = null;
31855         }
31856         if(ce.cls){
31857             el.addClass(ce.cls);
31858             removeCls = ce.cls;
31859         }
31860         if(ce.title){
31861             tipTitle.update(ce.title);
31862             tipTitle.show();
31863         }else{
31864             tipTitle.update('');
31865             tipTitle.hide();
31866         }
31867         el.dom.style.width  = tm.maxWidth+'px';
31868         //tipBody.dom.style.width = '';
31869         tipBodyText.update(o.text);
31870         var p = getPad(), w = ce.width;
31871         if(!w){
31872             var td = tipBodyText.dom;
31873             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31874             if(aw > tm.maxWidth){
31875                 w = tm.maxWidth;
31876             }else if(aw < tm.minWidth){
31877                 w = tm.minWidth;
31878             }else{
31879                 w = aw;
31880             }
31881         }
31882         //tipBody.setWidth(w);
31883         el.setWidth(parseInt(w, 10) + p);
31884         if(ce.autoHide === false){
31885             close.setDisplayed(true);
31886             if(dd){
31887                 dd.unlock();
31888             }
31889         }else{
31890             close.setDisplayed(false);
31891             if(dd){
31892                 dd.lock();
31893             }
31894         }
31895         if(xy){
31896             el.avoidY = xy[1]-18;
31897             el.setXY(xy);
31898         }
31899         if(tm.animate){
31900             el.setOpacity(.1);
31901             el.setStyle("visibility", "visible");
31902             el.fadeIn({callback: afterShow});
31903         }else{
31904             afterShow();
31905         }
31906     };
31907     
31908     var afterShow = function(){
31909         if(ce){
31910             el.show();
31911             esc.enable();
31912             if(tm.autoDismiss && ce.autoHide !== false){
31913                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31914             }
31915         }
31916     };
31917     
31918     var hide = function(noanim){
31919         clearTimeout(dismissProc);
31920         clearTimeout(hideProc);
31921         ce = null;
31922         if(el.isVisible()){
31923             esc.disable();
31924             if(noanim !== true && tm.animate){
31925                 el.fadeOut({callback: afterHide});
31926             }else{
31927                 afterHide();
31928             } 
31929         }
31930     };
31931     
31932     var afterHide = function(){
31933         el.hide();
31934         if(removeCls){
31935             el.removeClass(removeCls);
31936             removeCls = null;
31937         }
31938     };
31939     
31940     return {
31941         /**
31942         * @cfg {Number} minWidth
31943         * The minimum width of the quick tip (defaults to 40)
31944         */
31945        minWidth : 40,
31946         /**
31947         * @cfg {Number} maxWidth
31948         * The maximum width of the quick tip (defaults to 300)
31949         */
31950        maxWidth : 300,
31951         /**
31952         * @cfg {Boolean} interceptTitles
31953         * True to automatically use the element's DOM title value if available (defaults to false)
31954         */
31955        interceptTitles : false,
31956         /**
31957         * @cfg {Boolean} trackMouse
31958         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31959         */
31960        trackMouse : false,
31961         /**
31962         * @cfg {Boolean} hideOnClick
31963         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31964         */
31965        hideOnClick : true,
31966         /**
31967         * @cfg {Number} showDelay
31968         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31969         */
31970        showDelay : 500,
31971         /**
31972         * @cfg {Number} hideDelay
31973         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31974         */
31975        hideDelay : 200,
31976         /**
31977         * @cfg {Boolean} autoHide
31978         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31979         * Used in conjunction with hideDelay.
31980         */
31981        autoHide : true,
31982         /**
31983         * @cfg {Boolean}
31984         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31985         * (defaults to true).  Used in conjunction with autoDismissDelay.
31986         */
31987        autoDismiss : true,
31988         /**
31989         * @cfg {Number}
31990         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31991         */
31992        autoDismissDelay : 5000,
31993        /**
31994         * @cfg {Boolean} animate
31995         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31996         */
31997        animate : false,
31998
31999        /**
32000         * @cfg {String} title
32001         * Title text to display (defaults to '').  This can be any valid HTML markup.
32002         */
32003         title: '',
32004        /**
32005         * @cfg {String} text
32006         * Body text to display (defaults to '').  This can be any valid HTML markup.
32007         */
32008         text : '',
32009        /**
32010         * @cfg {String} cls
32011         * A CSS class to apply to the base quick tip element (defaults to '').
32012         */
32013         cls : '',
32014        /**
32015         * @cfg {Number} width
32016         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32017         * minWidth or maxWidth.
32018         */
32019         width : null,
32020
32021     /**
32022      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32023      * or display QuickTips in a page.
32024      */
32025        init : function(){
32026           tm = Roo.QuickTips;
32027           cfg = tm.tagConfig;
32028           if(!inited){
32029               if(!Roo.isReady){ // allow calling of init() before onReady
32030                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32031                   return;
32032               }
32033               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32034               el.fxDefaults = {stopFx: true};
32035               // maximum custom styling
32036               //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>');
32037               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>');              
32038               tipTitle = el.child('h3');
32039               tipTitle.enableDisplayMode("block");
32040               tipBody = el.child('div.x-tip-bd');
32041               tipBodyText = el.child('div.x-tip-bd-inner');
32042               //bdLeft = el.child('div.x-tip-bd-left');
32043               //bdRight = el.child('div.x-tip-bd-right');
32044               close = el.child('div.x-tip-close');
32045               close.enableDisplayMode("block");
32046               close.on("click", hide);
32047               var d = Roo.get(document);
32048               d.on("mousedown", onDown);
32049               d.on("mouseover", onOver);
32050               d.on("mouseout", onOut);
32051               d.on("mousemove", onMove);
32052               esc = d.addKeyListener(27, hide);
32053               esc.disable();
32054               if(Roo.dd.DD){
32055                   dd = el.initDD("default", null, {
32056                       onDrag : function(){
32057                           el.sync();  
32058                       }
32059                   });
32060                   dd.setHandleElId(tipTitle.id);
32061                   dd.lock();
32062               }
32063               inited = true;
32064           }
32065           this.enable(); 
32066        },
32067
32068     /**
32069      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32070      * are supported:
32071      * <pre>
32072 Property    Type                   Description
32073 ----------  ---------------------  ------------------------------------------------------------------------
32074 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32075      * </ul>
32076      * @param {Object} config The config object
32077      */
32078        register : function(config){
32079            var cs = config instanceof Array ? config : arguments;
32080            for(var i = 0, len = cs.length; i < len; i++) {
32081                var c = cs[i];
32082                var target = c.target;
32083                if(target){
32084                    if(target instanceof Array){
32085                        for(var j = 0, jlen = target.length; j < jlen; j++){
32086                            tagEls[target[j]] = c;
32087                        }
32088                    }else{
32089                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32090                    }
32091                }
32092            }
32093        },
32094
32095     /**
32096      * Removes this quick tip from its element and destroys it.
32097      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32098      */
32099        unregister : function(el){
32100            delete tagEls[Roo.id(el)];
32101        },
32102
32103     /**
32104      * Enable this quick tip.
32105      */
32106        enable : function(){
32107            if(inited && disabled){
32108                locks.pop();
32109                if(locks.length < 1){
32110                    disabled = false;
32111                }
32112            }
32113        },
32114
32115     /**
32116      * Disable this quick tip.
32117      */
32118        disable : function(){
32119           disabled = true;
32120           clearTimeout(showProc);
32121           clearTimeout(hideProc);
32122           clearTimeout(dismissProc);
32123           if(ce){
32124               hide(true);
32125           }
32126           locks.push(1);
32127        },
32128
32129     /**
32130      * Returns true if the quick tip is enabled, else false.
32131      */
32132        isEnabled : function(){
32133             return !disabled;
32134        },
32135
32136         // private
32137        tagConfig : {
32138            namespace : "ext",
32139            attribute : "qtip",
32140            width : "width",
32141            target : "target",
32142            title : "qtitle",
32143            hide : "hide",
32144            cls : "qclass"
32145        }
32146    };
32147 }();
32148
32149 // backwards compat
32150 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32151  * Based on:
32152  * Ext JS Library 1.1.1
32153  * Copyright(c) 2006-2007, Ext JS, LLC.
32154  *
32155  * Originally Released Under LGPL - original licence link has changed is not relivant.
32156  *
32157  * Fork - LGPL
32158  * <script type="text/javascript">
32159  */
32160  
32161
32162 /**
32163  * @class Roo.tree.TreePanel
32164  * @extends Roo.data.Tree
32165
32166  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32167  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32168  * @cfg {Boolean} enableDD true to enable drag and drop
32169  * @cfg {Boolean} enableDrag true to enable just drag
32170  * @cfg {Boolean} enableDrop true to enable just drop
32171  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32172  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32173  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32174  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32175  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32176  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32177  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32178  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32179  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32180  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32181  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32182  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32183  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32184  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32185  * @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>
32186  * @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>
32187  * 
32188  * @constructor
32189  * @param {String/HTMLElement/Element} el The container element
32190  * @param {Object} config
32191  */
32192 Roo.tree.TreePanel = function(el, config){
32193     var root = false;
32194     var loader = false;
32195     if (config.root) {
32196         root = config.root;
32197         delete config.root;
32198     }
32199     if (config.loader) {
32200         loader = config.loader;
32201         delete config.loader;
32202     }
32203     
32204     Roo.apply(this, config);
32205     Roo.tree.TreePanel.superclass.constructor.call(this);
32206     this.el = Roo.get(el);
32207     this.el.addClass('x-tree');
32208     //console.log(root);
32209     if (root) {
32210         this.setRootNode( Roo.factory(root, Roo.tree));
32211     }
32212     if (loader) {
32213         this.loader = Roo.factory(loader, Roo.tree);
32214     }
32215    /**
32216     * Read-only. The id of the container element becomes this TreePanel's id.
32217     */
32218     this.id = this.el.id;
32219     this.addEvents({
32220         /**
32221         * @event beforeload
32222         * Fires before a node is loaded, return false to cancel
32223         * @param {Node} node The node being loaded
32224         */
32225         "beforeload" : true,
32226         /**
32227         * @event load
32228         * Fires when a node is loaded
32229         * @param {Node} node The node that was loaded
32230         */
32231         "load" : true,
32232         /**
32233         * @event textchange
32234         * Fires when the text for a node is changed
32235         * @param {Node} node The node
32236         * @param {String} text The new text
32237         * @param {String} oldText The old text
32238         */
32239         "textchange" : true,
32240         /**
32241         * @event beforeexpand
32242         * Fires before a node is expanded, return false to cancel.
32243         * @param {Node} node The node
32244         * @param {Boolean} deep
32245         * @param {Boolean} anim
32246         */
32247         "beforeexpand" : true,
32248         /**
32249         * @event beforecollapse
32250         * Fires before a node is collapsed, return false to cancel.
32251         * @param {Node} node The node
32252         * @param {Boolean} deep
32253         * @param {Boolean} anim
32254         */
32255         "beforecollapse" : true,
32256         /**
32257         * @event expand
32258         * Fires when a node is expanded
32259         * @param {Node} node The node
32260         */
32261         "expand" : true,
32262         /**
32263         * @event disabledchange
32264         * Fires when the disabled status of a node changes
32265         * @param {Node} node The node
32266         * @param {Boolean} disabled
32267         */
32268         "disabledchange" : true,
32269         /**
32270         * @event collapse
32271         * Fires when a node is collapsed
32272         * @param {Node} node The node
32273         */
32274         "collapse" : true,
32275         /**
32276         * @event beforeclick
32277         * Fires before click processing on a node. Return false to cancel the default action.
32278         * @param {Node} node The node
32279         * @param {Roo.EventObject} e The event object
32280         */
32281         "beforeclick":true,
32282         /**
32283         * @event checkchange
32284         * Fires when a node with a checkbox's checked property changes
32285         * @param {Node} this This node
32286         * @param {Boolean} checked
32287         */
32288         "checkchange":true,
32289         /**
32290         * @event click
32291         * Fires when a node is clicked
32292         * @param {Node} node The node
32293         * @param {Roo.EventObject} e The event object
32294         */
32295         "click":true,
32296         /**
32297         * @event dblclick
32298         * Fires when a node is double clicked
32299         * @param {Node} node The node
32300         * @param {Roo.EventObject} e The event object
32301         */
32302         "dblclick":true,
32303         /**
32304         * @event contextmenu
32305         * Fires when a node is right clicked
32306         * @param {Node} node The node
32307         * @param {Roo.EventObject} e The event object
32308         */
32309         "contextmenu":true,
32310         /**
32311         * @event beforechildrenrendered
32312         * Fires right before the child nodes for a node are rendered
32313         * @param {Node} node The node
32314         */
32315         "beforechildrenrendered":true,
32316         /**
32317         * @event startdrag
32318         * Fires when a node starts being dragged
32319         * @param {Roo.tree.TreePanel} this
32320         * @param {Roo.tree.TreeNode} node
32321         * @param {event} e The raw browser event
32322         */ 
32323        "startdrag" : true,
32324        /**
32325         * @event enddrag
32326         * Fires when a drag operation is complete
32327         * @param {Roo.tree.TreePanel} this
32328         * @param {Roo.tree.TreeNode} node
32329         * @param {event} e The raw browser event
32330         */
32331        "enddrag" : true,
32332        /**
32333         * @event dragdrop
32334         * Fires when a dragged node is dropped on a valid DD target
32335         * @param {Roo.tree.TreePanel} this
32336         * @param {Roo.tree.TreeNode} node
32337         * @param {DD} dd The dd it was dropped on
32338         * @param {event} e The raw browser event
32339         */
32340        "dragdrop" : true,
32341        /**
32342         * @event beforenodedrop
32343         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32344         * passed to handlers has the following properties:<br />
32345         * <ul style="padding:5px;padding-left:16px;">
32346         * <li>tree - The TreePanel</li>
32347         * <li>target - The node being targeted for the drop</li>
32348         * <li>data - The drag data from the drag source</li>
32349         * <li>point - The point of the drop - append, above or below</li>
32350         * <li>source - The drag source</li>
32351         * <li>rawEvent - Raw mouse event</li>
32352         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32353         * to be inserted by setting them on this object.</li>
32354         * <li>cancel - Set this to true to cancel the drop.</li>
32355         * </ul>
32356         * @param {Object} dropEvent
32357         */
32358        "beforenodedrop" : true,
32359        /**
32360         * @event nodedrop
32361         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32362         * passed to handlers has the following properties:<br />
32363         * <ul style="padding:5px;padding-left:16px;">
32364         * <li>tree - The TreePanel</li>
32365         * <li>target - The node being targeted for the drop</li>
32366         * <li>data - The drag data from the drag source</li>
32367         * <li>point - The point of the drop - append, above or below</li>
32368         * <li>source - The drag source</li>
32369         * <li>rawEvent - Raw mouse event</li>
32370         * <li>dropNode - Dropped node(s).</li>
32371         * </ul>
32372         * @param {Object} dropEvent
32373         */
32374        "nodedrop" : true,
32375         /**
32376         * @event nodedragover
32377         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32378         * passed to handlers has the following properties:<br />
32379         * <ul style="padding:5px;padding-left:16px;">
32380         * <li>tree - The TreePanel</li>
32381         * <li>target - The node being targeted for the drop</li>
32382         * <li>data - The drag data from the drag source</li>
32383         * <li>point - The point of the drop - append, above or below</li>
32384         * <li>source - The drag source</li>
32385         * <li>rawEvent - Raw mouse event</li>
32386         * <li>dropNode - Drop node(s) provided by the source.</li>
32387         * <li>cancel - Set this to true to signal drop not allowed.</li>
32388         * </ul>
32389         * @param {Object} dragOverEvent
32390         */
32391        "nodedragover" : true
32392         
32393     });
32394     if(this.singleExpand){
32395        this.on("beforeexpand", this.restrictExpand, this);
32396     }
32397     if (this.editor) {
32398         this.editor.tree = this;
32399         this.editor = Roo.factory(this.editor, Roo.tree);
32400     }
32401     
32402     if (this.selModel) {
32403         this.selModel = Roo.factory(this.selModel, Roo.tree);
32404     }
32405    
32406 };
32407 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32408     rootVisible : true,
32409     animate: Roo.enableFx,
32410     lines : true,
32411     enableDD : false,
32412     hlDrop : Roo.enableFx,
32413   
32414     renderer: false,
32415     
32416     rendererTip: false,
32417     // private
32418     restrictExpand : function(node){
32419         var p = node.parentNode;
32420         if(p){
32421             if(p.expandedChild && p.expandedChild.parentNode == p){
32422                 p.expandedChild.collapse();
32423             }
32424             p.expandedChild = node;
32425         }
32426     },
32427
32428     // private override
32429     setRootNode : function(node){
32430         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32431         if(!this.rootVisible){
32432             node.ui = new Roo.tree.RootTreeNodeUI(node);
32433         }
32434         return node;
32435     },
32436
32437     /**
32438      * Returns the container element for this TreePanel
32439      */
32440     getEl : function(){
32441         return this.el;
32442     },
32443
32444     /**
32445      * Returns the default TreeLoader for this TreePanel
32446      */
32447     getLoader : function(){
32448         return this.loader;
32449     },
32450
32451     /**
32452      * Expand all nodes
32453      */
32454     expandAll : function(){
32455         this.root.expand(true);
32456     },
32457
32458     /**
32459      * Collapse all nodes
32460      */
32461     collapseAll : function(){
32462         this.root.collapse(true);
32463     },
32464
32465     /**
32466      * Returns the selection model used by this TreePanel
32467      */
32468     getSelectionModel : function(){
32469         if(!this.selModel){
32470             this.selModel = new Roo.tree.DefaultSelectionModel();
32471         }
32472         return this.selModel;
32473     },
32474
32475     /**
32476      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32477      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32478      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32479      * @return {Array}
32480      */
32481     getChecked : function(a, startNode){
32482         startNode = startNode || this.root;
32483         var r = [];
32484         var f = function(){
32485             if(this.attributes.checked){
32486                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32487             }
32488         }
32489         startNode.cascade(f);
32490         return r;
32491     },
32492
32493     /**
32494      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32495      * @param {String} path
32496      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32497      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32498      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32499      */
32500     expandPath : function(path, attr, callback){
32501         attr = attr || "id";
32502         var keys = path.split(this.pathSeparator);
32503         var curNode = this.root;
32504         if(curNode.attributes[attr] != keys[1]){ // invalid root
32505             if(callback){
32506                 callback(false, null);
32507             }
32508             return;
32509         }
32510         var index = 1;
32511         var f = function(){
32512             if(++index == keys.length){
32513                 if(callback){
32514                     callback(true, curNode);
32515                 }
32516                 return;
32517             }
32518             var c = curNode.findChild(attr, keys[index]);
32519             if(!c){
32520                 if(callback){
32521                     callback(false, curNode);
32522                 }
32523                 return;
32524             }
32525             curNode = c;
32526             c.expand(false, false, f);
32527         };
32528         curNode.expand(false, false, f);
32529     },
32530
32531     /**
32532      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32533      * @param {String} path
32534      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32535      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32536      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32537      */
32538     selectPath : function(path, attr, callback){
32539         attr = attr || "id";
32540         var keys = path.split(this.pathSeparator);
32541         var v = keys.pop();
32542         if(keys.length > 0){
32543             var f = function(success, node){
32544                 if(success && node){
32545                     var n = node.findChild(attr, v);
32546                     if(n){
32547                         n.select();
32548                         if(callback){
32549                             callback(true, n);
32550                         }
32551                     }else if(callback){
32552                         callback(false, n);
32553                     }
32554                 }else{
32555                     if(callback){
32556                         callback(false, n);
32557                     }
32558                 }
32559             };
32560             this.expandPath(keys.join(this.pathSeparator), attr, f);
32561         }else{
32562             this.root.select();
32563             if(callback){
32564                 callback(true, this.root);
32565             }
32566         }
32567     },
32568
32569     getTreeEl : function(){
32570         return this.el;
32571     },
32572
32573     /**
32574      * Trigger rendering of this TreePanel
32575      */
32576     render : function(){
32577         if (this.innerCt) {
32578             return this; // stop it rendering more than once!!
32579         }
32580         
32581         this.innerCt = this.el.createChild({tag:"ul",
32582                cls:"x-tree-root-ct " +
32583                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32584
32585         if(this.containerScroll){
32586             Roo.dd.ScrollManager.register(this.el);
32587         }
32588         if((this.enableDD || this.enableDrop) && !this.dropZone){
32589            /**
32590             * The dropZone used by this tree if drop is enabled
32591             * @type Roo.tree.TreeDropZone
32592             */
32593              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32594                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32595            });
32596         }
32597         if((this.enableDD || this.enableDrag) && !this.dragZone){
32598            /**
32599             * The dragZone used by this tree if drag is enabled
32600             * @type Roo.tree.TreeDragZone
32601             */
32602             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32603                ddGroup: this.ddGroup || "TreeDD",
32604                scroll: this.ddScroll
32605            });
32606         }
32607         this.getSelectionModel().init(this);
32608         if (!this.root) {
32609             Roo.log("ROOT not set in tree");
32610             return this;
32611         }
32612         this.root.render();
32613         if(!this.rootVisible){
32614             this.root.renderChildren();
32615         }
32616         return this;
32617     }
32618 });/*
32619  * Based on:
32620  * Ext JS Library 1.1.1
32621  * Copyright(c) 2006-2007, Ext JS, LLC.
32622  *
32623  * Originally Released Under LGPL - original licence link has changed is not relivant.
32624  *
32625  * Fork - LGPL
32626  * <script type="text/javascript">
32627  */
32628  
32629
32630 /**
32631  * @class Roo.tree.DefaultSelectionModel
32632  * @extends Roo.util.Observable
32633  * The default single selection for a TreePanel.
32634  * @param {Object} cfg Configuration
32635  */
32636 Roo.tree.DefaultSelectionModel = function(cfg){
32637    this.selNode = null;
32638    
32639    
32640    
32641    this.addEvents({
32642        /**
32643         * @event selectionchange
32644         * Fires when the selected node changes
32645         * @param {DefaultSelectionModel} this
32646         * @param {TreeNode} node the new selection
32647         */
32648        "selectionchange" : true,
32649
32650        /**
32651         * @event beforeselect
32652         * Fires before the selected node changes, return false to cancel the change
32653         * @param {DefaultSelectionModel} this
32654         * @param {TreeNode} node the new selection
32655         * @param {TreeNode} node the old selection
32656         */
32657        "beforeselect" : true
32658    });
32659    
32660     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32661 };
32662
32663 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32664     init : function(tree){
32665         this.tree = tree;
32666         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32667         tree.on("click", this.onNodeClick, this);
32668     },
32669     
32670     onNodeClick : function(node, e){
32671         if (e.ctrlKey && this.selNode == node)  {
32672             this.unselect(node);
32673             return;
32674         }
32675         this.select(node);
32676     },
32677     
32678     /**
32679      * Select a node.
32680      * @param {TreeNode} node The node to select
32681      * @return {TreeNode} The selected node
32682      */
32683     select : function(node){
32684         var last = this.selNode;
32685         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32686             if(last){
32687                 last.ui.onSelectedChange(false);
32688             }
32689             this.selNode = node;
32690             node.ui.onSelectedChange(true);
32691             this.fireEvent("selectionchange", this, node, last);
32692         }
32693         return node;
32694     },
32695     
32696     /**
32697      * Deselect a node.
32698      * @param {TreeNode} node The node to unselect
32699      */
32700     unselect : function(node){
32701         if(this.selNode == node){
32702             this.clearSelections();
32703         }    
32704     },
32705     
32706     /**
32707      * Clear all selections
32708      */
32709     clearSelections : function(){
32710         var n = this.selNode;
32711         if(n){
32712             n.ui.onSelectedChange(false);
32713             this.selNode = null;
32714             this.fireEvent("selectionchange", this, null);
32715         }
32716         return n;
32717     },
32718     
32719     /**
32720      * Get the selected node
32721      * @return {TreeNode} The selected node
32722      */
32723     getSelectedNode : function(){
32724         return this.selNode;    
32725     },
32726     
32727     /**
32728      * Returns true if the node is selected
32729      * @param {TreeNode} node The node to check
32730      * @return {Boolean}
32731      */
32732     isSelected : function(node){
32733         return this.selNode == node;  
32734     },
32735
32736     /**
32737      * Selects the node above the selected node in the tree, intelligently walking the nodes
32738      * @return TreeNode The new selection
32739      */
32740     selectPrevious : function(){
32741         var s = this.selNode || this.lastSelNode;
32742         if(!s){
32743             return null;
32744         }
32745         var ps = s.previousSibling;
32746         if(ps){
32747             if(!ps.isExpanded() || ps.childNodes.length < 1){
32748                 return this.select(ps);
32749             } else{
32750                 var lc = ps.lastChild;
32751                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32752                     lc = lc.lastChild;
32753                 }
32754                 return this.select(lc);
32755             }
32756         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32757             return this.select(s.parentNode);
32758         }
32759         return null;
32760     },
32761
32762     /**
32763      * Selects the node above the selected node in the tree, intelligently walking the nodes
32764      * @return TreeNode The new selection
32765      */
32766     selectNext : function(){
32767         var s = this.selNode || this.lastSelNode;
32768         if(!s){
32769             return null;
32770         }
32771         if(s.firstChild && s.isExpanded()){
32772              return this.select(s.firstChild);
32773          }else if(s.nextSibling){
32774              return this.select(s.nextSibling);
32775          }else if(s.parentNode){
32776             var newS = null;
32777             s.parentNode.bubble(function(){
32778                 if(this.nextSibling){
32779                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32780                     return false;
32781                 }
32782             });
32783             return newS;
32784          }
32785         return null;
32786     },
32787
32788     onKeyDown : function(e){
32789         var s = this.selNode || this.lastSelNode;
32790         // undesirable, but required
32791         var sm = this;
32792         if(!s){
32793             return;
32794         }
32795         var k = e.getKey();
32796         switch(k){
32797              case e.DOWN:
32798                  e.stopEvent();
32799                  this.selectNext();
32800              break;
32801              case e.UP:
32802                  e.stopEvent();
32803                  this.selectPrevious();
32804              break;
32805              case e.RIGHT:
32806                  e.preventDefault();
32807                  if(s.hasChildNodes()){
32808                      if(!s.isExpanded()){
32809                          s.expand();
32810                      }else if(s.firstChild){
32811                          this.select(s.firstChild, e);
32812                      }
32813                  }
32814              break;
32815              case e.LEFT:
32816                  e.preventDefault();
32817                  if(s.hasChildNodes() && s.isExpanded()){
32818                      s.collapse();
32819                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32820                      this.select(s.parentNode, e);
32821                  }
32822              break;
32823         };
32824     }
32825 });
32826
32827 /**
32828  * @class Roo.tree.MultiSelectionModel
32829  * @extends Roo.util.Observable
32830  * Multi selection for a TreePanel.
32831  * @param {Object} cfg Configuration
32832  */
32833 Roo.tree.MultiSelectionModel = function(){
32834    this.selNodes = [];
32835    this.selMap = {};
32836    this.addEvents({
32837        /**
32838         * @event selectionchange
32839         * Fires when the selected nodes change
32840         * @param {MultiSelectionModel} this
32841         * @param {Array} nodes Array of the selected nodes
32842         */
32843        "selectionchange" : true
32844    });
32845    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32846    
32847 };
32848
32849 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32850     init : function(tree){
32851         this.tree = tree;
32852         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32853         tree.on("click", this.onNodeClick, this);
32854     },
32855     
32856     onNodeClick : function(node, e){
32857         this.select(node, e, e.ctrlKey);
32858     },
32859     
32860     /**
32861      * Select a node.
32862      * @param {TreeNode} node The node to select
32863      * @param {EventObject} e (optional) An event associated with the selection
32864      * @param {Boolean} keepExisting True to retain existing selections
32865      * @return {TreeNode} The selected node
32866      */
32867     select : function(node, e, keepExisting){
32868         if(keepExisting !== true){
32869             this.clearSelections(true);
32870         }
32871         if(this.isSelected(node)){
32872             this.lastSelNode = node;
32873             return node;
32874         }
32875         this.selNodes.push(node);
32876         this.selMap[node.id] = node;
32877         this.lastSelNode = node;
32878         node.ui.onSelectedChange(true);
32879         this.fireEvent("selectionchange", this, this.selNodes);
32880         return node;
32881     },
32882     
32883     /**
32884      * Deselect a node.
32885      * @param {TreeNode} node The node to unselect
32886      */
32887     unselect : function(node){
32888         if(this.selMap[node.id]){
32889             node.ui.onSelectedChange(false);
32890             var sn = this.selNodes;
32891             var index = -1;
32892             if(sn.indexOf){
32893                 index = sn.indexOf(node);
32894             }else{
32895                 for(var i = 0, len = sn.length; i < len; i++){
32896                     if(sn[i] == node){
32897                         index = i;
32898                         break;
32899                     }
32900                 }
32901             }
32902             if(index != -1){
32903                 this.selNodes.splice(index, 1);
32904             }
32905             delete this.selMap[node.id];
32906             this.fireEvent("selectionchange", this, this.selNodes);
32907         }
32908     },
32909     
32910     /**
32911      * Clear all selections
32912      */
32913     clearSelections : function(suppressEvent){
32914         var sn = this.selNodes;
32915         if(sn.length > 0){
32916             for(var i = 0, len = sn.length; i < len; i++){
32917                 sn[i].ui.onSelectedChange(false);
32918             }
32919             this.selNodes = [];
32920             this.selMap = {};
32921             if(suppressEvent !== true){
32922                 this.fireEvent("selectionchange", this, this.selNodes);
32923             }
32924         }
32925     },
32926     
32927     /**
32928      * Returns true if the node is selected
32929      * @param {TreeNode} node The node to check
32930      * @return {Boolean}
32931      */
32932     isSelected : function(node){
32933         return this.selMap[node.id] ? true : false;  
32934     },
32935     
32936     /**
32937      * Returns an array of the selected nodes
32938      * @return {Array}
32939      */
32940     getSelectedNodes : function(){
32941         return this.selNodes;    
32942     },
32943
32944     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32945
32946     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32947
32948     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32949 });/*
32950  * Based on:
32951  * Ext JS Library 1.1.1
32952  * Copyright(c) 2006-2007, Ext JS, LLC.
32953  *
32954  * Originally Released Under LGPL - original licence link has changed is not relivant.
32955  *
32956  * Fork - LGPL
32957  * <script type="text/javascript">
32958  */
32959  
32960 /**
32961  * @class Roo.tree.TreeNode
32962  * @extends Roo.data.Node
32963  * @cfg {String} text The text for this node
32964  * @cfg {Boolean} expanded true to start the node expanded
32965  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32966  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32967  * @cfg {Boolean} disabled true to start the node disabled
32968  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32969  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32970  * @cfg {String} cls A css class to be added to the node
32971  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32972  * @cfg {String} href URL of the link used for the node (defaults to #)
32973  * @cfg {String} hrefTarget target frame for the link
32974  * @cfg {String} qtip An Ext QuickTip for the node
32975  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32976  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32977  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32978  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32979  * (defaults to undefined with no checkbox rendered)
32980  * @constructor
32981  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32982  */
32983 Roo.tree.TreeNode = function(attributes){
32984     attributes = attributes || {};
32985     if(typeof attributes == "string"){
32986         attributes = {text: attributes};
32987     }
32988     this.childrenRendered = false;
32989     this.rendered = false;
32990     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32991     this.expanded = attributes.expanded === true;
32992     this.isTarget = attributes.isTarget !== false;
32993     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32994     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32995
32996     /**
32997      * Read-only. The text for this node. To change it use setText().
32998      * @type String
32999      */
33000     this.text = attributes.text;
33001     /**
33002      * True if this node is disabled.
33003      * @type Boolean
33004      */
33005     this.disabled = attributes.disabled === true;
33006
33007     this.addEvents({
33008         /**
33009         * @event textchange
33010         * Fires when the text for this node is changed
33011         * @param {Node} this This node
33012         * @param {String} text The new text
33013         * @param {String} oldText The old text
33014         */
33015         "textchange" : true,
33016         /**
33017         * @event beforeexpand
33018         * Fires before this node is expanded, return false to cancel.
33019         * @param {Node} this This node
33020         * @param {Boolean} deep
33021         * @param {Boolean} anim
33022         */
33023         "beforeexpand" : true,
33024         /**
33025         * @event beforecollapse
33026         * Fires before this node is collapsed, return false to cancel.
33027         * @param {Node} this This node
33028         * @param {Boolean} deep
33029         * @param {Boolean} anim
33030         */
33031         "beforecollapse" : true,
33032         /**
33033         * @event expand
33034         * Fires when this node is expanded
33035         * @param {Node} this This node
33036         */
33037         "expand" : true,
33038         /**
33039         * @event disabledchange
33040         * Fires when the disabled status of this node changes
33041         * @param {Node} this This node
33042         * @param {Boolean} disabled
33043         */
33044         "disabledchange" : true,
33045         /**
33046         * @event collapse
33047         * Fires when this node is collapsed
33048         * @param {Node} this This node
33049         */
33050         "collapse" : true,
33051         /**
33052         * @event beforeclick
33053         * Fires before click processing. Return false to cancel the default action.
33054         * @param {Node} this This node
33055         * @param {Roo.EventObject} e The event object
33056         */
33057         "beforeclick":true,
33058         /**
33059         * @event checkchange
33060         * Fires when a node with a checkbox's checked property changes
33061         * @param {Node} this This node
33062         * @param {Boolean} checked
33063         */
33064         "checkchange":true,
33065         /**
33066         * @event click
33067         * Fires when this node is clicked
33068         * @param {Node} this This node
33069         * @param {Roo.EventObject} e The event object
33070         */
33071         "click":true,
33072         /**
33073         * @event dblclick
33074         * Fires when this node is double clicked
33075         * @param {Node} this This node
33076         * @param {Roo.EventObject} e The event object
33077         */
33078         "dblclick":true,
33079         /**
33080         * @event contextmenu
33081         * Fires when this node is right clicked
33082         * @param {Node} this This node
33083         * @param {Roo.EventObject} e The event object
33084         */
33085         "contextmenu":true,
33086         /**
33087         * @event beforechildrenrendered
33088         * Fires right before the child nodes for this node are rendered
33089         * @param {Node} this This node
33090         */
33091         "beforechildrenrendered":true
33092     });
33093
33094     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33095
33096     /**
33097      * Read-only. The UI for this node
33098      * @type TreeNodeUI
33099      */
33100     this.ui = new uiClass(this);
33101     
33102     // finally support items[]
33103     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33104         return;
33105     }
33106     
33107     
33108     Roo.each(this.attributes.items, function(c) {
33109         this.appendChild(Roo.factory(c,Roo.Tree));
33110     }, this);
33111     delete this.attributes.items;
33112     
33113     
33114     
33115 };
33116 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33117     preventHScroll: true,
33118     /**
33119      * Returns true if this node is expanded
33120      * @return {Boolean}
33121      */
33122     isExpanded : function(){
33123         return this.expanded;
33124     },
33125
33126     /**
33127      * Returns the UI object for this node
33128      * @return {TreeNodeUI}
33129      */
33130     getUI : function(){
33131         return this.ui;
33132     },
33133
33134     // private override
33135     setFirstChild : function(node){
33136         var of = this.firstChild;
33137         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33138         if(this.childrenRendered && of && node != of){
33139             of.renderIndent(true, true);
33140         }
33141         if(this.rendered){
33142             this.renderIndent(true, true);
33143         }
33144     },
33145
33146     // private override
33147     setLastChild : function(node){
33148         var ol = this.lastChild;
33149         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33150         if(this.childrenRendered && ol && node != ol){
33151             ol.renderIndent(true, true);
33152         }
33153         if(this.rendered){
33154             this.renderIndent(true, true);
33155         }
33156     },
33157
33158     // these methods are overridden to provide lazy rendering support
33159     // private override
33160     appendChild : function()
33161     {
33162         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33163         if(node && this.childrenRendered){
33164             node.render();
33165         }
33166         this.ui.updateExpandIcon();
33167         return node;
33168     },
33169
33170     // private override
33171     removeChild : function(node){
33172         this.ownerTree.getSelectionModel().unselect(node);
33173         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33174         // if it's been rendered remove dom node
33175         if(this.childrenRendered){
33176             node.ui.remove();
33177         }
33178         if(this.childNodes.length < 1){
33179             this.collapse(false, false);
33180         }else{
33181             this.ui.updateExpandIcon();
33182         }
33183         if(!this.firstChild) {
33184             this.childrenRendered = false;
33185         }
33186         return node;
33187     },
33188
33189     // private override
33190     insertBefore : function(node, refNode){
33191         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33192         if(newNode && refNode && this.childrenRendered){
33193             node.render();
33194         }
33195         this.ui.updateExpandIcon();
33196         return newNode;
33197     },
33198
33199     /**
33200      * Sets the text for this node
33201      * @param {String} text
33202      */
33203     setText : function(text){
33204         var oldText = this.text;
33205         this.text = text;
33206         this.attributes.text = text;
33207         if(this.rendered){ // event without subscribing
33208             this.ui.onTextChange(this, text, oldText);
33209         }
33210         this.fireEvent("textchange", this, text, oldText);
33211     },
33212
33213     /**
33214      * Triggers selection of this node
33215      */
33216     select : function(){
33217         this.getOwnerTree().getSelectionModel().select(this);
33218     },
33219
33220     /**
33221      * Triggers deselection of this node
33222      */
33223     unselect : function(){
33224         this.getOwnerTree().getSelectionModel().unselect(this);
33225     },
33226
33227     /**
33228      * Returns true if this node is selected
33229      * @return {Boolean}
33230      */
33231     isSelected : function(){
33232         return this.getOwnerTree().getSelectionModel().isSelected(this);
33233     },
33234
33235     /**
33236      * Expand this node.
33237      * @param {Boolean} deep (optional) True to expand all children as well
33238      * @param {Boolean} anim (optional) false to cancel the default animation
33239      * @param {Function} callback (optional) A callback to be called when
33240      * expanding this node completes (does not wait for deep expand to complete).
33241      * Called with 1 parameter, this node.
33242      */
33243     expand : function(deep, anim, callback){
33244         if(!this.expanded){
33245             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33246                 return;
33247             }
33248             if(!this.childrenRendered){
33249                 this.renderChildren();
33250             }
33251             this.expanded = true;
33252             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33253                 this.ui.animExpand(function(){
33254                     this.fireEvent("expand", this);
33255                     if(typeof callback == "function"){
33256                         callback(this);
33257                     }
33258                     if(deep === true){
33259                         this.expandChildNodes(true);
33260                     }
33261                 }.createDelegate(this));
33262                 return;
33263             }else{
33264                 this.ui.expand();
33265                 this.fireEvent("expand", this);
33266                 if(typeof callback == "function"){
33267                     callback(this);
33268                 }
33269             }
33270         }else{
33271            if(typeof callback == "function"){
33272                callback(this);
33273            }
33274         }
33275         if(deep === true){
33276             this.expandChildNodes(true);
33277         }
33278     },
33279
33280     isHiddenRoot : function(){
33281         return this.isRoot && !this.getOwnerTree().rootVisible;
33282     },
33283
33284     /**
33285      * Collapse this node.
33286      * @param {Boolean} deep (optional) True to collapse all children as well
33287      * @param {Boolean} anim (optional) false to cancel the default animation
33288      */
33289     collapse : function(deep, anim){
33290         if(this.expanded && !this.isHiddenRoot()){
33291             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33292                 return;
33293             }
33294             this.expanded = false;
33295             if((this.getOwnerTree().animate && anim !== false) || anim){
33296                 this.ui.animCollapse(function(){
33297                     this.fireEvent("collapse", this);
33298                     if(deep === true){
33299                         this.collapseChildNodes(true);
33300                     }
33301                 }.createDelegate(this));
33302                 return;
33303             }else{
33304                 this.ui.collapse();
33305                 this.fireEvent("collapse", this);
33306             }
33307         }
33308         if(deep === true){
33309             var cs = this.childNodes;
33310             for(var i = 0, len = cs.length; i < len; i++) {
33311                 cs[i].collapse(true, false);
33312             }
33313         }
33314     },
33315
33316     // private
33317     delayedExpand : function(delay){
33318         if(!this.expandProcId){
33319             this.expandProcId = this.expand.defer(delay, this);
33320         }
33321     },
33322
33323     // private
33324     cancelExpand : function(){
33325         if(this.expandProcId){
33326             clearTimeout(this.expandProcId);
33327         }
33328         this.expandProcId = false;
33329     },
33330
33331     /**
33332      * Toggles expanded/collapsed state of the node
33333      */
33334     toggle : function(){
33335         if(this.expanded){
33336             this.collapse();
33337         }else{
33338             this.expand();
33339         }
33340     },
33341
33342     /**
33343      * Ensures all parent nodes are expanded
33344      */
33345     ensureVisible : function(callback){
33346         var tree = this.getOwnerTree();
33347         tree.expandPath(this.parentNode.getPath(), false, function(){
33348             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33349             Roo.callback(callback);
33350         }.createDelegate(this));
33351     },
33352
33353     /**
33354      * Expand all child nodes
33355      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33356      */
33357     expandChildNodes : function(deep){
33358         var cs = this.childNodes;
33359         for(var i = 0, len = cs.length; i < len; i++) {
33360                 cs[i].expand(deep);
33361         }
33362     },
33363
33364     /**
33365      * Collapse all child nodes
33366      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33367      */
33368     collapseChildNodes : function(deep){
33369         var cs = this.childNodes;
33370         for(var i = 0, len = cs.length; i < len; i++) {
33371                 cs[i].collapse(deep);
33372         }
33373     },
33374
33375     /**
33376      * Disables this node
33377      */
33378     disable : function(){
33379         this.disabled = true;
33380         this.unselect();
33381         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33382             this.ui.onDisableChange(this, true);
33383         }
33384         this.fireEvent("disabledchange", this, true);
33385     },
33386
33387     /**
33388      * Enables this node
33389      */
33390     enable : function(){
33391         this.disabled = false;
33392         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33393             this.ui.onDisableChange(this, false);
33394         }
33395         this.fireEvent("disabledchange", this, false);
33396     },
33397
33398     // private
33399     renderChildren : function(suppressEvent){
33400         if(suppressEvent !== false){
33401             this.fireEvent("beforechildrenrendered", this);
33402         }
33403         var cs = this.childNodes;
33404         for(var i = 0, len = cs.length; i < len; i++){
33405             cs[i].render(true);
33406         }
33407         this.childrenRendered = true;
33408     },
33409
33410     // private
33411     sort : function(fn, scope){
33412         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33413         if(this.childrenRendered){
33414             var cs = this.childNodes;
33415             for(var i = 0, len = cs.length; i < len; i++){
33416                 cs[i].render(true);
33417             }
33418         }
33419     },
33420
33421     // private
33422     render : function(bulkRender){
33423         this.ui.render(bulkRender);
33424         if(!this.rendered){
33425             this.rendered = true;
33426             if(this.expanded){
33427                 this.expanded = false;
33428                 this.expand(false, false);
33429             }
33430         }
33431     },
33432
33433     // private
33434     renderIndent : function(deep, refresh){
33435         if(refresh){
33436             this.ui.childIndent = null;
33437         }
33438         this.ui.renderIndent();
33439         if(deep === true && this.childrenRendered){
33440             var cs = this.childNodes;
33441             for(var i = 0, len = cs.length; i < len; i++){
33442                 cs[i].renderIndent(true, refresh);
33443             }
33444         }
33445     }
33446 });/*
33447  * Based on:
33448  * Ext JS Library 1.1.1
33449  * Copyright(c) 2006-2007, Ext JS, LLC.
33450  *
33451  * Originally Released Under LGPL - original licence link has changed is not relivant.
33452  *
33453  * Fork - LGPL
33454  * <script type="text/javascript">
33455  */
33456  
33457 /**
33458  * @class Roo.tree.AsyncTreeNode
33459  * @extends Roo.tree.TreeNode
33460  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33461  * @constructor
33462  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33463  */
33464  Roo.tree.AsyncTreeNode = function(config){
33465     this.loaded = false;
33466     this.loading = false;
33467     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33468     /**
33469     * @event beforeload
33470     * Fires before this node is loaded, return false to cancel
33471     * @param {Node} this This node
33472     */
33473     this.addEvents({'beforeload':true, 'load': true});
33474     /**
33475     * @event load
33476     * Fires when this node is loaded
33477     * @param {Node} this This node
33478     */
33479     /**
33480      * The loader used by this node (defaults to using the tree's defined loader)
33481      * @type TreeLoader
33482      * @property loader
33483      */
33484 };
33485 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33486     expand : function(deep, anim, callback){
33487         if(this.loading){ // if an async load is already running, waiting til it's done
33488             var timer;
33489             var f = function(){
33490                 if(!this.loading){ // done loading
33491                     clearInterval(timer);
33492                     this.expand(deep, anim, callback);
33493                 }
33494             }.createDelegate(this);
33495             timer = setInterval(f, 200);
33496             return;
33497         }
33498         if(!this.loaded){
33499             if(this.fireEvent("beforeload", this) === false){
33500                 return;
33501             }
33502             this.loading = true;
33503             this.ui.beforeLoad(this);
33504             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33505             if(loader){
33506                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33507                 return;
33508             }
33509         }
33510         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33511     },
33512     
33513     /**
33514      * Returns true if this node is currently loading
33515      * @return {Boolean}
33516      */
33517     isLoading : function(){
33518         return this.loading;  
33519     },
33520     
33521     loadComplete : function(deep, anim, callback){
33522         this.loading = false;
33523         this.loaded = true;
33524         this.ui.afterLoad(this);
33525         this.fireEvent("load", this);
33526         this.expand(deep, anim, callback);
33527     },
33528     
33529     /**
33530      * Returns true if this node has been loaded
33531      * @return {Boolean}
33532      */
33533     isLoaded : function(){
33534         return this.loaded;
33535     },
33536     
33537     hasChildNodes : function(){
33538         if(!this.isLeaf() && !this.loaded){
33539             return true;
33540         }else{
33541             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33542         }
33543     },
33544
33545     /**
33546      * Trigger a reload for this node
33547      * @param {Function} callback
33548      */
33549     reload : function(callback){
33550         this.collapse(false, false);
33551         while(this.firstChild){
33552             this.removeChild(this.firstChild);
33553         }
33554         this.childrenRendered = false;
33555         this.loaded = false;
33556         if(this.isHiddenRoot()){
33557             this.expanded = false;
33558         }
33559         this.expand(false, false, callback);
33560     }
33561 });/*
33562  * Based on:
33563  * Ext JS Library 1.1.1
33564  * Copyright(c) 2006-2007, Ext JS, LLC.
33565  *
33566  * Originally Released Under LGPL - original licence link has changed is not relivant.
33567  *
33568  * Fork - LGPL
33569  * <script type="text/javascript">
33570  */
33571  
33572 /**
33573  * @class Roo.tree.TreeNodeUI
33574  * @constructor
33575  * @param {Object} node The node to render
33576  * The TreeNode UI implementation is separate from the
33577  * tree implementation. Unless you are customizing the tree UI,
33578  * you should never have to use this directly.
33579  */
33580 Roo.tree.TreeNodeUI = function(node){
33581     this.node = node;
33582     this.rendered = false;
33583     this.animating = false;
33584     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33585 };
33586
33587 Roo.tree.TreeNodeUI.prototype = {
33588     removeChild : function(node){
33589         if(this.rendered){
33590             this.ctNode.removeChild(node.ui.getEl());
33591         }
33592     },
33593
33594     beforeLoad : function(){
33595          this.addClass("x-tree-node-loading");
33596     },
33597
33598     afterLoad : function(){
33599          this.removeClass("x-tree-node-loading");
33600     },
33601
33602     onTextChange : function(node, text, oldText){
33603         if(this.rendered){
33604             this.textNode.innerHTML = text;
33605         }
33606     },
33607
33608     onDisableChange : function(node, state){
33609         this.disabled = state;
33610         if(state){
33611             this.addClass("x-tree-node-disabled");
33612         }else{
33613             this.removeClass("x-tree-node-disabled");
33614         }
33615     },
33616
33617     onSelectedChange : function(state){
33618         if(state){
33619             this.focus();
33620             this.addClass("x-tree-selected");
33621         }else{
33622             //this.blur();
33623             this.removeClass("x-tree-selected");
33624         }
33625     },
33626
33627     onMove : function(tree, node, oldParent, newParent, index, refNode){
33628         this.childIndent = null;
33629         if(this.rendered){
33630             var targetNode = newParent.ui.getContainer();
33631             if(!targetNode){//target not rendered
33632                 this.holder = document.createElement("div");
33633                 this.holder.appendChild(this.wrap);
33634                 return;
33635             }
33636             var insertBefore = refNode ? refNode.ui.getEl() : null;
33637             if(insertBefore){
33638                 targetNode.insertBefore(this.wrap, insertBefore);
33639             }else{
33640                 targetNode.appendChild(this.wrap);
33641             }
33642             this.node.renderIndent(true);
33643         }
33644     },
33645
33646     addClass : function(cls){
33647         if(this.elNode){
33648             Roo.fly(this.elNode).addClass(cls);
33649         }
33650     },
33651
33652     removeClass : function(cls){
33653         if(this.elNode){
33654             Roo.fly(this.elNode).removeClass(cls);
33655         }
33656     },
33657
33658     remove : function(){
33659         if(this.rendered){
33660             this.holder = document.createElement("div");
33661             this.holder.appendChild(this.wrap);
33662         }
33663     },
33664
33665     fireEvent : function(){
33666         return this.node.fireEvent.apply(this.node, arguments);
33667     },
33668
33669     initEvents : function(){
33670         this.node.on("move", this.onMove, this);
33671         var E = Roo.EventManager;
33672         var a = this.anchor;
33673
33674         var el = Roo.fly(a, '_treeui');
33675
33676         if(Roo.isOpera){ // opera render bug ignores the CSS
33677             el.setStyle("text-decoration", "none");
33678         }
33679
33680         el.on("click", this.onClick, this);
33681         el.on("dblclick", this.onDblClick, this);
33682
33683         if(this.checkbox){
33684             Roo.EventManager.on(this.checkbox,
33685                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33686         }
33687
33688         el.on("contextmenu", this.onContextMenu, this);
33689
33690         var icon = Roo.fly(this.iconNode);
33691         icon.on("click", this.onClick, this);
33692         icon.on("dblclick", this.onDblClick, this);
33693         icon.on("contextmenu", this.onContextMenu, this);
33694         E.on(this.ecNode, "click", this.ecClick, this, true);
33695
33696         if(this.node.disabled){
33697             this.addClass("x-tree-node-disabled");
33698         }
33699         if(this.node.hidden){
33700             this.addClass("x-tree-node-disabled");
33701         }
33702         var ot = this.node.getOwnerTree();
33703         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33704         if(dd && (!this.node.isRoot || ot.rootVisible)){
33705             Roo.dd.Registry.register(this.elNode, {
33706                 node: this.node,
33707                 handles: this.getDDHandles(),
33708                 isHandle: false
33709             });
33710         }
33711     },
33712
33713     getDDHandles : function(){
33714         return [this.iconNode, this.textNode];
33715     },
33716
33717     hide : function(){
33718         if(this.rendered){
33719             this.wrap.style.display = "none";
33720         }
33721     },
33722
33723     show : function(){
33724         if(this.rendered){
33725             this.wrap.style.display = "";
33726         }
33727     },
33728
33729     onContextMenu : function(e){
33730         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33731             e.preventDefault();
33732             this.focus();
33733             this.fireEvent("contextmenu", this.node, e);
33734         }
33735     },
33736
33737     onClick : function(e){
33738         if(this.dropping){
33739             e.stopEvent();
33740             return;
33741         }
33742         if(this.fireEvent("beforeclick", this.node, e) !== false){
33743             if(!this.disabled && this.node.attributes.href){
33744                 this.fireEvent("click", this.node, e);
33745                 return;
33746             }
33747             e.preventDefault();
33748             if(this.disabled){
33749                 return;
33750             }
33751
33752             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33753                 this.node.toggle();
33754             }
33755
33756             this.fireEvent("click", this.node, e);
33757         }else{
33758             e.stopEvent();
33759         }
33760     },
33761
33762     onDblClick : function(e){
33763         e.preventDefault();
33764         if(this.disabled){
33765             return;
33766         }
33767         if(this.checkbox){
33768             this.toggleCheck();
33769         }
33770         if(!this.animating && this.node.hasChildNodes()){
33771             this.node.toggle();
33772         }
33773         this.fireEvent("dblclick", this.node, e);
33774     },
33775
33776     onCheckChange : function(){
33777         var checked = this.checkbox.checked;
33778         this.node.attributes.checked = checked;
33779         this.fireEvent('checkchange', this.node, checked);
33780     },
33781
33782     ecClick : function(e){
33783         if(!this.animating && this.node.hasChildNodes()){
33784             this.node.toggle();
33785         }
33786     },
33787
33788     startDrop : function(){
33789         this.dropping = true;
33790     },
33791
33792     // delayed drop so the click event doesn't get fired on a drop
33793     endDrop : function(){
33794        setTimeout(function(){
33795            this.dropping = false;
33796        }.createDelegate(this), 50);
33797     },
33798
33799     expand : function(){
33800         this.updateExpandIcon();
33801         this.ctNode.style.display = "";
33802     },
33803
33804     focus : function(){
33805         if(!this.node.preventHScroll){
33806             try{this.anchor.focus();
33807             }catch(e){}
33808         }else if(!Roo.isIE){
33809             try{
33810                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33811                 var l = noscroll.scrollLeft;
33812                 this.anchor.focus();
33813                 noscroll.scrollLeft = l;
33814             }catch(e){}
33815         }
33816     },
33817
33818     toggleCheck : function(value){
33819         var cb = this.checkbox;
33820         if(cb){
33821             cb.checked = (value === undefined ? !cb.checked : value);
33822         }
33823     },
33824
33825     blur : function(){
33826         try{
33827             this.anchor.blur();
33828         }catch(e){}
33829     },
33830
33831     animExpand : function(callback){
33832         var ct = Roo.get(this.ctNode);
33833         ct.stopFx();
33834         if(!this.node.hasChildNodes()){
33835             this.updateExpandIcon();
33836             this.ctNode.style.display = "";
33837             Roo.callback(callback);
33838             return;
33839         }
33840         this.animating = true;
33841         this.updateExpandIcon();
33842
33843         ct.slideIn('t', {
33844            callback : function(){
33845                this.animating = false;
33846                Roo.callback(callback);
33847             },
33848             scope: this,
33849             duration: this.node.ownerTree.duration || .25
33850         });
33851     },
33852
33853     highlight : function(){
33854         var tree = this.node.getOwnerTree();
33855         Roo.fly(this.wrap).highlight(
33856             tree.hlColor || "C3DAF9",
33857             {endColor: tree.hlBaseColor}
33858         );
33859     },
33860
33861     collapse : function(){
33862         this.updateExpandIcon();
33863         this.ctNode.style.display = "none";
33864     },
33865
33866     animCollapse : function(callback){
33867         var ct = Roo.get(this.ctNode);
33868         ct.enableDisplayMode('block');
33869         ct.stopFx();
33870
33871         this.animating = true;
33872         this.updateExpandIcon();
33873
33874         ct.slideOut('t', {
33875             callback : function(){
33876                this.animating = false;
33877                Roo.callback(callback);
33878             },
33879             scope: this,
33880             duration: this.node.ownerTree.duration || .25
33881         });
33882     },
33883
33884     getContainer : function(){
33885         return this.ctNode;
33886     },
33887
33888     getEl : function(){
33889         return this.wrap;
33890     },
33891
33892     appendDDGhost : function(ghostNode){
33893         ghostNode.appendChild(this.elNode.cloneNode(true));
33894     },
33895
33896     getDDRepairXY : function(){
33897         return Roo.lib.Dom.getXY(this.iconNode);
33898     },
33899
33900     onRender : function(){
33901         this.render();
33902     },
33903
33904     render : function(bulkRender){
33905         var n = this.node, a = n.attributes;
33906         var targetNode = n.parentNode ?
33907               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33908
33909         if(!this.rendered){
33910             this.rendered = true;
33911
33912             this.renderElements(n, a, targetNode, bulkRender);
33913
33914             if(a.qtip){
33915                if(this.textNode.setAttributeNS){
33916                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33917                    if(a.qtipTitle){
33918                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33919                    }
33920                }else{
33921                    this.textNode.setAttribute("ext:qtip", a.qtip);
33922                    if(a.qtipTitle){
33923                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33924                    }
33925                }
33926             }else if(a.qtipCfg){
33927                 a.qtipCfg.target = Roo.id(this.textNode);
33928                 Roo.QuickTips.register(a.qtipCfg);
33929             }
33930             this.initEvents();
33931             if(!this.node.expanded){
33932                 this.updateExpandIcon();
33933             }
33934         }else{
33935             if(bulkRender === true) {
33936                 targetNode.appendChild(this.wrap);
33937             }
33938         }
33939     },
33940
33941     renderElements : function(n, a, targetNode, bulkRender)
33942     {
33943         // add some indent caching, this helps performance when rendering a large tree
33944         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33945         var t = n.getOwnerTree();
33946         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33947         if (typeof(n.attributes.html) != 'undefined') {
33948             txt = n.attributes.html;
33949         }
33950         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33951         var cb = typeof a.checked == 'boolean';
33952         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33953         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33954             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33955             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33956             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33957             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33958             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33959              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33960                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33961             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33962             "</li>"];
33963
33964         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33965             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33966                                 n.nextSibling.ui.getEl(), buf.join(""));
33967         }else{
33968             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33969         }
33970
33971         this.elNode = this.wrap.childNodes[0];
33972         this.ctNode = this.wrap.childNodes[1];
33973         var cs = this.elNode.childNodes;
33974         this.indentNode = cs[0];
33975         this.ecNode = cs[1];
33976         this.iconNode = cs[2];
33977         var index = 3;
33978         if(cb){
33979             this.checkbox = cs[3];
33980             index++;
33981         }
33982         this.anchor = cs[index];
33983         this.textNode = cs[index].firstChild;
33984     },
33985
33986     getAnchor : function(){
33987         return this.anchor;
33988     },
33989
33990     getTextEl : function(){
33991         return this.textNode;
33992     },
33993
33994     getIconEl : function(){
33995         return this.iconNode;
33996     },
33997
33998     isChecked : function(){
33999         return this.checkbox ? this.checkbox.checked : false;
34000     },
34001
34002     updateExpandIcon : function(){
34003         if(this.rendered){
34004             var n = this.node, c1, c2;
34005             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34006             var hasChild = n.hasChildNodes();
34007             if(hasChild){
34008                 if(n.expanded){
34009                     cls += "-minus";
34010                     c1 = "x-tree-node-collapsed";
34011                     c2 = "x-tree-node-expanded";
34012                 }else{
34013                     cls += "-plus";
34014                     c1 = "x-tree-node-expanded";
34015                     c2 = "x-tree-node-collapsed";
34016                 }
34017                 if(this.wasLeaf){
34018                     this.removeClass("x-tree-node-leaf");
34019                     this.wasLeaf = false;
34020                 }
34021                 if(this.c1 != c1 || this.c2 != c2){
34022                     Roo.fly(this.elNode).replaceClass(c1, c2);
34023                     this.c1 = c1; this.c2 = c2;
34024                 }
34025             }else{
34026                 // this changes non-leafs into leafs if they have no children.
34027                 // it's not very rational behaviour..
34028                 
34029                 if(!this.wasLeaf && this.node.leaf){
34030                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34031                     delete this.c1;
34032                     delete this.c2;
34033                     this.wasLeaf = true;
34034                 }
34035             }
34036             var ecc = "x-tree-ec-icon "+cls;
34037             if(this.ecc != ecc){
34038                 this.ecNode.className = ecc;
34039                 this.ecc = ecc;
34040             }
34041         }
34042     },
34043
34044     getChildIndent : function(){
34045         if(!this.childIndent){
34046             var buf = [];
34047             var p = this.node;
34048             while(p){
34049                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34050                     if(!p.isLast()) {
34051                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34052                     } else {
34053                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34054                     }
34055                 }
34056                 p = p.parentNode;
34057             }
34058             this.childIndent = buf.join("");
34059         }
34060         return this.childIndent;
34061     },
34062
34063     renderIndent : function(){
34064         if(this.rendered){
34065             var indent = "";
34066             var p = this.node.parentNode;
34067             if(p){
34068                 indent = p.ui.getChildIndent();
34069             }
34070             if(this.indentMarkup != indent){ // don't rerender if not required
34071                 this.indentNode.innerHTML = indent;
34072                 this.indentMarkup = indent;
34073             }
34074             this.updateExpandIcon();
34075         }
34076     }
34077 };
34078
34079 Roo.tree.RootTreeNodeUI = function(){
34080     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34081 };
34082 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34083     render : function(){
34084         if(!this.rendered){
34085             var targetNode = this.node.ownerTree.innerCt.dom;
34086             this.node.expanded = true;
34087             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34088             this.wrap = this.ctNode = targetNode.firstChild;
34089         }
34090     },
34091     collapse : function(){
34092     },
34093     expand : function(){
34094     }
34095 });/*
34096  * Based on:
34097  * Ext JS Library 1.1.1
34098  * Copyright(c) 2006-2007, Ext JS, LLC.
34099  *
34100  * Originally Released Under LGPL - original licence link has changed is not relivant.
34101  *
34102  * Fork - LGPL
34103  * <script type="text/javascript">
34104  */
34105 /**
34106  * @class Roo.tree.TreeLoader
34107  * @extends Roo.util.Observable
34108  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34109  * nodes from a specified URL. The response must be a javascript Array definition
34110  * who's elements are node definition objects. eg:
34111  * <pre><code>
34112 {  success : true,
34113    data :      [
34114    
34115     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34116     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34117     ]
34118 }
34119
34120
34121 </code></pre>
34122  * <br><br>
34123  * The old style respose with just an array is still supported, but not recommended.
34124  * <br><br>
34125  *
34126  * A server request is sent, and child nodes are loaded only when a node is expanded.
34127  * The loading node's id is passed to the server under the parameter name "node" to
34128  * enable the server to produce the correct child nodes.
34129  * <br><br>
34130  * To pass extra parameters, an event handler may be attached to the "beforeload"
34131  * event, and the parameters specified in the TreeLoader's baseParams property:
34132  * <pre><code>
34133     myTreeLoader.on("beforeload", function(treeLoader, node) {
34134         this.baseParams.category = node.attributes.category;
34135     }, this);
34136 </code></pre><
34137  * This would pass an HTTP parameter called "category" to the server containing
34138  * the value of the Node's "category" attribute.
34139  * @constructor
34140  * Creates a new Treeloader.
34141  * @param {Object} config A config object containing config properties.
34142  */
34143 Roo.tree.TreeLoader = function(config){
34144     this.baseParams = {};
34145     this.requestMethod = "POST";
34146     Roo.apply(this, config);
34147
34148     this.addEvents({
34149     
34150         /**
34151          * @event beforeload
34152          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34153          * @param {Object} This TreeLoader object.
34154          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34155          * @param {Object} callback The callback function specified in the {@link #load} call.
34156          */
34157         beforeload : true,
34158         /**
34159          * @event load
34160          * Fires when the node has been successfuly loaded.
34161          * @param {Object} This TreeLoader object.
34162          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34163          * @param {Object} response The response object containing the data from the server.
34164          */
34165         load : true,
34166         /**
34167          * @event loadexception
34168          * Fires if the network request failed.
34169          * @param {Object} This TreeLoader object.
34170          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34171          * @param {Object} response The response object containing the data from the server.
34172          */
34173         loadexception : true,
34174         /**
34175          * @event create
34176          * Fires before a node is created, enabling you to return custom Node types 
34177          * @param {Object} This TreeLoader object.
34178          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34179          */
34180         create : true
34181     });
34182
34183     Roo.tree.TreeLoader.superclass.constructor.call(this);
34184 };
34185
34186 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34187     /**
34188     * @cfg {String} dataUrl The URL from which to request a Json string which
34189     * specifies an array of node definition object representing the child nodes
34190     * to be loaded.
34191     */
34192     /**
34193     * @cfg {String} requestMethod either GET or POST
34194     * defaults to POST (due to BC)
34195     * to be loaded.
34196     */
34197     /**
34198     * @cfg {Object} baseParams (optional) An object containing properties which
34199     * specify HTTP parameters to be passed to each request for child nodes.
34200     */
34201     /**
34202     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34203     * created by this loader. If the attributes sent by the server have an attribute in this object,
34204     * they take priority.
34205     */
34206     /**
34207     * @cfg {Object} uiProviders (optional) An object containing properties which
34208     * 
34209     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34210     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34211     * <i>uiProvider</i> attribute of a returned child node is a string rather
34212     * than a reference to a TreeNodeUI implementation, this that string value
34213     * is used as a property name in the uiProviders object. You can define the provider named
34214     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34215     */
34216     uiProviders : {},
34217
34218     /**
34219     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34220     * child nodes before loading.
34221     */
34222     clearOnLoad : true,
34223
34224     /**
34225     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34226     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34227     * Grid query { data : [ .....] }
34228     */
34229     
34230     root : false,
34231      /**
34232     * @cfg {String} queryParam (optional) 
34233     * Name of the query as it will be passed on the querystring (defaults to 'node')
34234     * eg. the request will be ?node=[id]
34235     */
34236     
34237     
34238     queryParam: false,
34239     
34240     /**
34241      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34242      * This is called automatically when a node is expanded, but may be used to reload
34243      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34244      * @param {Roo.tree.TreeNode} node
34245      * @param {Function} callback
34246      */
34247     load : function(node, callback){
34248         if(this.clearOnLoad){
34249             while(node.firstChild){
34250                 node.removeChild(node.firstChild);
34251             }
34252         }
34253         if(node.attributes.children){ // preloaded json children
34254             var cs = node.attributes.children;
34255             for(var i = 0, len = cs.length; i < len; i++){
34256                 node.appendChild(this.createNode(cs[i]));
34257             }
34258             if(typeof callback == "function"){
34259                 callback();
34260             }
34261         }else if(this.dataUrl){
34262             this.requestData(node, callback);
34263         }
34264     },
34265
34266     getParams: function(node){
34267         var buf = [], bp = this.baseParams;
34268         for(var key in bp){
34269             if(typeof bp[key] != "function"){
34270                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34271             }
34272         }
34273         var n = this.queryParam === false ? 'node' : this.queryParam;
34274         buf.push(n + "=", encodeURIComponent(node.id));
34275         return buf.join("");
34276     },
34277
34278     requestData : function(node, callback){
34279         if(this.fireEvent("beforeload", this, node, callback) !== false){
34280             this.transId = Roo.Ajax.request({
34281                 method:this.requestMethod,
34282                 url: this.dataUrl||this.url,
34283                 success: this.handleResponse,
34284                 failure: this.handleFailure,
34285                 scope: this,
34286                 argument: {callback: callback, node: node},
34287                 params: this.getParams(node)
34288             });
34289         }else{
34290             // if the load is cancelled, make sure we notify
34291             // the node that we are done
34292             if(typeof callback == "function"){
34293                 callback();
34294             }
34295         }
34296     },
34297
34298     isLoading : function(){
34299         return this.transId ? true : false;
34300     },
34301
34302     abort : function(){
34303         if(this.isLoading()){
34304             Roo.Ajax.abort(this.transId);
34305         }
34306     },
34307
34308     // private
34309     createNode : function(attr)
34310     {
34311         // apply baseAttrs, nice idea Corey!
34312         if(this.baseAttrs){
34313             Roo.applyIf(attr, this.baseAttrs);
34314         }
34315         if(this.applyLoader !== false){
34316             attr.loader = this;
34317         }
34318         // uiProvider = depreciated..
34319         
34320         if(typeof(attr.uiProvider) == 'string'){
34321            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34322                 /**  eval:var:attr */ eval(attr.uiProvider);
34323         }
34324         if(typeof(this.uiProviders['default']) != 'undefined') {
34325             attr.uiProvider = this.uiProviders['default'];
34326         }
34327         
34328         this.fireEvent('create', this, attr);
34329         
34330         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34331         return(attr.leaf ?
34332                         new Roo.tree.TreeNode(attr) :
34333                         new Roo.tree.AsyncTreeNode(attr));
34334     },
34335
34336     processResponse : function(response, node, callback)
34337     {
34338         var json = response.responseText;
34339         try {
34340             
34341             var o = Roo.decode(json);
34342             
34343             if (this.root === false && typeof(o.success) != undefined) {
34344                 this.root = 'data'; // the default behaviour for list like data..
34345                 }
34346                 
34347             if (this.root !== false &&  !o.success) {
34348                 // it's a failure condition.
34349                 var a = response.argument;
34350                 this.fireEvent("loadexception", this, a.node, response);
34351                 Roo.log("Load failed - should have a handler really");
34352                 return;
34353             }
34354             
34355             
34356             
34357             if (this.root !== false) {
34358                  o = o[this.root];
34359             }
34360             
34361             for(var i = 0, len = o.length; i < len; i++){
34362                 var n = this.createNode(o[i]);
34363                 if(n){
34364                     node.appendChild(n);
34365                 }
34366             }
34367             if(typeof callback == "function"){
34368                 callback(this, node);
34369             }
34370         }catch(e){
34371             this.handleFailure(response);
34372         }
34373     },
34374
34375     handleResponse : function(response){
34376         this.transId = false;
34377         var a = response.argument;
34378         this.processResponse(response, a.node, a.callback);
34379         this.fireEvent("load", this, a.node, response);
34380     },
34381
34382     handleFailure : function(response)
34383     {
34384         // should handle failure better..
34385         this.transId = false;
34386         var a = response.argument;
34387         this.fireEvent("loadexception", this, a.node, response);
34388         if(typeof a.callback == "function"){
34389             a.callback(this, a.node);
34390         }
34391     }
34392 });/*
34393  * Based on:
34394  * Ext JS Library 1.1.1
34395  * Copyright(c) 2006-2007, Ext JS, LLC.
34396  *
34397  * Originally Released Under LGPL - original licence link has changed is not relivant.
34398  *
34399  * Fork - LGPL
34400  * <script type="text/javascript">
34401  */
34402
34403 /**
34404 * @class Roo.tree.TreeFilter
34405 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34406 * @param {TreePanel} tree
34407 * @param {Object} config (optional)
34408  */
34409 Roo.tree.TreeFilter = function(tree, config){
34410     this.tree = tree;
34411     this.filtered = {};
34412     Roo.apply(this, config);
34413 };
34414
34415 Roo.tree.TreeFilter.prototype = {
34416     clearBlank:false,
34417     reverse:false,
34418     autoClear:false,
34419     remove:false,
34420
34421      /**
34422      * Filter the data by a specific attribute.
34423      * @param {String/RegExp} value Either string that the attribute value
34424      * should start with or a RegExp to test against the attribute
34425      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34426      * @param {TreeNode} startNode (optional) The node to start the filter at.
34427      */
34428     filter : function(value, attr, startNode){
34429         attr = attr || "text";
34430         var f;
34431         if(typeof value == "string"){
34432             var vlen = value.length;
34433             // auto clear empty filter
34434             if(vlen == 0 && this.clearBlank){
34435                 this.clear();
34436                 return;
34437             }
34438             value = value.toLowerCase();
34439             f = function(n){
34440                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34441             };
34442         }else if(value.exec){ // regex?
34443             f = function(n){
34444                 return value.test(n.attributes[attr]);
34445             };
34446         }else{
34447             throw 'Illegal filter type, must be string or regex';
34448         }
34449         this.filterBy(f, null, startNode);
34450         },
34451
34452     /**
34453      * Filter by a function. The passed function will be called with each
34454      * node in the tree (or from the startNode). If the function returns true, the node is kept
34455      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34456      * @param {Function} fn The filter function
34457      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34458      */
34459     filterBy : function(fn, scope, startNode){
34460         startNode = startNode || this.tree.root;
34461         if(this.autoClear){
34462             this.clear();
34463         }
34464         var af = this.filtered, rv = this.reverse;
34465         var f = function(n){
34466             if(n == startNode){
34467                 return true;
34468             }
34469             if(af[n.id]){
34470                 return false;
34471             }
34472             var m = fn.call(scope || n, n);
34473             if(!m || rv){
34474                 af[n.id] = n;
34475                 n.ui.hide();
34476                 return false;
34477             }
34478             return true;
34479         };
34480         startNode.cascade(f);
34481         if(this.remove){
34482            for(var id in af){
34483                if(typeof id != "function"){
34484                    var n = af[id];
34485                    if(n && n.parentNode){
34486                        n.parentNode.removeChild(n);
34487                    }
34488                }
34489            }
34490         }
34491     },
34492
34493     /**
34494      * Clears the current filter. Note: with the "remove" option
34495      * set a filter cannot be cleared.
34496      */
34497     clear : function(){
34498         var t = this.tree;
34499         var af = this.filtered;
34500         for(var id in af){
34501             if(typeof id != "function"){
34502                 var n = af[id];
34503                 if(n){
34504                     n.ui.show();
34505                 }
34506             }
34507         }
34508         this.filtered = {};
34509     }
34510 };
34511 /*
34512  * Based on:
34513  * Ext JS Library 1.1.1
34514  * Copyright(c) 2006-2007, Ext JS, LLC.
34515  *
34516  * Originally Released Under LGPL - original licence link has changed is not relivant.
34517  *
34518  * Fork - LGPL
34519  * <script type="text/javascript">
34520  */
34521  
34522
34523 /**
34524  * @class Roo.tree.TreeSorter
34525  * Provides sorting of nodes in a TreePanel
34526  * 
34527  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34528  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34529  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34530  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34531  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34532  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34533  * @constructor
34534  * @param {TreePanel} tree
34535  * @param {Object} config
34536  */
34537 Roo.tree.TreeSorter = function(tree, config){
34538     Roo.apply(this, config);
34539     tree.on("beforechildrenrendered", this.doSort, this);
34540     tree.on("append", this.updateSort, this);
34541     tree.on("insert", this.updateSort, this);
34542     
34543     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34544     var p = this.property || "text";
34545     var sortType = this.sortType;
34546     var fs = this.folderSort;
34547     var cs = this.caseSensitive === true;
34548     var leafAttr = this.leafAttr || 'leaf';
34549
34550     this.sortFn = function(n1, n2){
34551         if(fs){
34552             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34553                 return 1;
34554             }
34555             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34556                 return -1;
34557             }
34558         }
34559         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34560         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34561         if(v1 < v2){
34562                         return dsc ? +1 : -1;
34563                 }else if(v1 > v2){
34564                         return dsc ? -1 : +1;
34565         }else{
34566                 return 0;
34567         }
34568     };
34569 };
34570
34571 Roo.tree.TreeSorter.prototype = {
34572     doSort : function(node){
34573         node.sort(this.sortFn);
34574     },
34575     
34576     compareNodes : function(n1, n2){
34577         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34578     },
34579     
34580     updateSort : function(tree, node){
34581         if(node.childrenRendered){
34582             this.doSort.defer(1, this, [node]);
34583         }
34584     }
34585 };/*
34586  * Based on:
34587  * Ext JS Library 1.1.1
34588  * Copyright(c) 2006-2007, Ext JS, LLC.
34589  *
34590  * Originally Released Under LGPL - original licence link has changed is not relivant.
34591  *
34592  * Fork - LGPL
34593  * <script type="text/javascript">
34594  */
34595
34596 if(Roo.dd.DropZone){
34597     
34598 Roo.tree.TreeDropZone = function(tree, config){
34599     this.allowParentInsert = false;
34600     this.allowContainerDrop = false;
34601     this.appendOnly = false;
34602     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34603     this.tree = tree;
34604     this.lastInsertClass = "x-tree-no-status";
34605     this.dragOverData = {};
34606 };
34607
34608 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34609     ddGroup : "TreeDD",
34610     scroll:  true,
34611     
34612     expandDelay : 1000,
34613     
34614     expandNode : function(node){
34615         if(node.hasChildNodes() && !node.isExpanded()){
34616             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34617         }
34618     },
34619     
34620     queueExpand : function(node){
34621         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34622     },
34623     
34624     cancelExpand : function(){
34625         if(this.expandProcId){
34626             clearTimeout(this.expandProcId);
34627             this.expandProcId = false;
34628         }
34629     },
34630     
34631     isValidDropPoint : function(n, pt, dd, e, data){
34632         if(!n || !data){ return false; }
34633         var targetNode = n.node;
34634         var dropNode = data.node;
34635         // default drop rules
34636         if(!(targetNode && targetNode.isTarget && pt)){
34637             return false;
34638         }
34639         if(pt == "append" && targetNode.allowChildren === false){
34640             return false;
34641         }
34642         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34643             return false;
34644         }
34645         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34646             return false;
34647         }
34648         // reuse the object
34649         var overEvent = this.dragOverData;
34650         overEvent.tree = this.tree;
34651         overEvent.target = targetNode;
34652         overEvent.data = data;
34653         overEvent.point = pt;
34654         overEvent.source = dd;
34655         overEvent.rawEvent = e;
34656         overEvent.dropNode = dropNode;
34657         overEvent.cancel = false;  
34658         var result = this.tree.fireEvent("nodedragover", overEvent);
34659         return overEvent.cancel === false && result !== false;
34660     },
34661     
34662     getDropPoint : function(e, n, dd)
34663     {
34664         var tn = n.node;
34665         if(tn.isRoot){
34666             return tn.allowChildren !== false ? "append" : false; // always append for root
34667         }
34668         var dragEl = n.ddel;
34669         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34670         var y = Roo.lib.Event.getPageY(e);
34671         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34672         
34673         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34674         var noAppend = tn.allowChildren === false;
34675         if(this.appendOnly || tn.parentNode.allowChildren === false){
34676             return noAppend ? false : "append";
34677         }
34678         var noBelow = false;
34679         if(!this.allowParentInsert){
34680             noBelow = tn.hasChildNodes() && tn.isExpanded();
34681         }
34682         var q = (b - t) / (noAppend ? 2 : 3);
34683         if(y >= t && y < (t + q)){
34684             return "above";
34685         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34686             return "below";
34687         }else{
34688             return "append";
34689         }
34690     },
34691     
34692     onNodeEnter : function(n, dd, e, data)
34693     {
34694         this.cancelExpand();
34695     },
34696     
34697     onNodeOver : function(n, dd, e, data)
34698     {
34699        
34700         var pt = this.getDropPoint(e, n, dd);
34701         var node = n.node;
34702         
34703         // auto node expand check
34704         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34705             this.queueExpand(node);
34706         }else if(pt != "append"){
34707             this.cancelExpand();
34708         }
34709         
34710         // set the insert point style on the target node
34711         var returnCls = this.dropNotAllowed;
34712         if(this.isValidDropPoint(n, pt, dd, e, data)){
34713            if(pt){
34714                var el = n.ddel;
34715                var cls;
34716                if(pt == "above"){
34717                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34718                    cls = "x-tree-drag-insert-above";
34719                }else if(pt == "below"){
34720                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34721                    cls = "x-tree-drag-insert-below";
34722                }else{
34723                    returnCls = "x-tree-drop-ok-append";
34724                    cls = "x-tree-drag-append";
34725                }
34726                if(this.lastInsertClass != cls){
34727                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34728                    this.lastInsertClass = cls;
34729                }
34730            }
34731        }
34732        return returnCls;
34733     },
34734     
34735     onNodeOut : function(n, dd, e, data){
34736         
34737         this.cancelExpand();
34738         this.removeDropIndicators(n);
34739     },
34740     
34741     onNodeDrop : function(n, dd, e, data){
34742         var point = this.getDropPoint(e, n, dd);
34743         var targetNode = n.node;
34744         targetNode.ui.startDrop();
34745         if(!this.isValidDropPoint(n, point, dd, e, data)){
34746             targetNode.ui.endDrop();
34747             return false;
34748         }
34749         // first try to find the drop node
34750         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34751         var dropEvent = {
34752             tree : this.tree,
34753             target: targetNode,
34754             data: data,
34755             point: point,
34756             source: dd,
34757             rawEvent: e,
34758             dropNode: dropNode,
34759             cancel: !dropNode   
34760         };
34761         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34762         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34763             targetNode.ui.endDrop();
34764             return false;
34765         }
34766         // allow target changing
34767         targetNode = dropEvent.target;
34768         if(point == "append" && !targetNode.isExpanded()){
34769             targetNode.expand(false, null, function(){
34770                 this.completeDrop(dropEvent);
34771             }.createDelegate(this));
34772         }else{
34773             this.completeDrop(dropEvent);
34774         }
34775         return true;
34776     },
34777     
34778     completeDrop : function(de){
34779         var ns = de.dropNode, p = de.point, t = de.target;
34780         if(!(ns instanceof Array)){
34781             ns = [ns];
34782         }
34783         var n;
34784         for(var i = 0, len = ns.length; i < len; i++){
34785             n = ns[i];
34786             if(p == "above"){
34787                 t.parentNode.insertBefore(n, t);
34788             }else if(p == "below"){
34789                 t.parentNode.insertBefore(n, t.nextSibling);
34790             }else{
34791                 t.appendChild(n);
34792             }
34793         }
34794         n.ui.focus();
34795         if(this.tree.hlDrop){
34796             n.ui.highlight();
34797         }
34798         t.ui.endDrop();
34799         this.tree.fireEvent("nodedrop", de);
34800     },
34801     
34802     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34803         if(this.tree.hlDrop){
34804             dropNode.ui.focus();
34805             dropNode.ui.highlight();
34806         }
34807         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34808     },
34809     
34810     getTree : function(){
34811         return this.tree;
34812     },
34813     
34814     removeDropIndicators : function(n){
34815         if(n && n.ddel){
34816             var el = n.ddel;
34817             Roo.fly(el).removeClass([
34818                     "x-tree-drag-insert-above",
34819                     "x-tree-drag-insert-below",
34820                     "x-tree-drag-append"]);
34821             this.lastInsertClass = "_noclass";
34822         }
34823     },
34824     
34825     beforeDragDrop : function(target, e, id){
34826         this.cancelExpand();
34827         return true;
34828     },
34829     
34830     afterRepair : function(data){
34831         if(data && Roo.enableFx){
34832             data.node.ui.highlight();
34833         }
34834         this.hideProxy();
34835     } 
34836     
34837 });
34838
34839 }
34840 /*
34841  * Based on:
34842  * Ext JS Library 1.1.1
34843  * Copyright(c) 2006-2007, Ext JS, LLC.
34844  *
34845  * Originally Released Under LGPL - original licence link has changed is not relivant.
34846  *
34847  * Fork - LGPL
34848  * <script type="text/javascript">
34849  */
34850  
34851
34852 if(Roo.dd.DragZone){
34853 Roo.tree.TreeDragZone = function(tree, config){
34854     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34855     this.tree = tree;
34856 };
34857
34858 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34859     ddGroup : "TreeDD",
34860    
34861     onBeforeDrag : function(data, e){
34862         var n = data.node;
34863         return n && n.draggable && !n.disabled;
34864     },
34865      
34866     
34867     onInitDrag : function(e){
34868         var data = this.dragData;
34869         this.tree.getSelectionModel().select(data.node);
34870         this.proxy.update("");
34871         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34872         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34873     },
34874     
34875     getRepairXY : function(e, data){
34876         return data.node.ui.getDDRepairXY();
34877     },
34878     
34879     onEndDrag : function(data, e){
34880         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34881         
34882         
34883     },
34884     
34885     onValidDrop : function(dd, e, id){
34886         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34887         this.hideProxy();
34888     },
34889     
34890     beforeInvalidDrop : function(e, id){
34891         // this scrolls the original position back into view
34892         var sm = this.tree.getSelectionModel();
34893         sm.clearSelections();
34894         sm.select(this.dragData.node);
34895     }
34896 });
34897 }/*
34898  * Based on:
34899  * Ext JS Library 1.1.1
34900  * Copyright(c) 2006-2007, Ext JS, LLC.
34901  *
34902  * Originally Released Under LGPL - original licence link has changed is not relivant.
34903  *
34904  * Fork - LGPL
34905  * <script type="text/javascript">
34906  */
34907 /**
34908  * @class Roo.tree.TreeEditor
34909  * @extends Roo.Editor
34910  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34911  * as the editor field.
34912  * @constructor
34913  * @param {Object} config (used to be the tree panel.)
34914  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34915  * 
34916  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34917  * @cfg {Roo.form.TextField|Object} field The field configuration
34918  *
34919  * 
34920  */
34921 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34922     var tree = config;
34923     var field;
34924     if (oldconfig) { // old style..
34925         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34926     } else {
34927         // new style..
34928         tree = config.tree;
34929         config.field = config.field  || {};
34930         config.field.xtype = 'TextField';
34931         field = Roo.factory(config.field, Roo.form);
34932     }
34933     config = config || {};
34934     
34935     
34936     this.addEvents({
34937         /**
34938          * @event beforenodeedit
34939          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34940          * false from the handler of this event.
34941          * @param {Editor} this
34942          * @param {Roo.tree.Node} node 
34943          */
34944         "beforenodeedit" : true
34945     });
34946     
34947     //Roo.log(config);
34948     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34949
34950     this.tree = tree;
34951
34952     tree.on('beforeclick', this.beforeNodeClick, this);
34953     tree.getTreeEl().on('mousedown', this.hide, this);
34954     this.on('complete', this.updateNode, this);
34955     this.on('beforestartedit', this.fitToTree, this);
34956     this.on('startedit', this.bindScroll, this, {delay:10});
34957     this.on('specialkey', this.onSpecialKey, this);
34958 };
34959
34960 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34961     /**
34962      * @cfg {String} alignment
34963      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34964      */
34965     alignment: "l-l",
34966     // inherit
34967     autoSize: false,
34968     /**
34969      * @cfg {Boolean} hideEl
34970      * True to hide the bound element while the editor is displayed (defaults to false)
34971      */
34972     hideEl : false,
34973     /**
34974      * @cfg {String} cls
34975      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34976      */
34977     cls: "x-small-editor x-tree-editor",
34978     /**
34979      * @cfg {Boolean} shim
34980      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34981      */
34982     shim:false,
34983     // inherit
34984     shadow:"frame",
34985     /**
34986      * @cfg {Number} maxWidth
34987      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34988      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34989      * scroll and client offsets into account prior to each edit.
34990      */
34991     maxWidth: 250,
34992
34993     editDelay : 350,
34994
34995     // private
34996     fitToTree : function(ed, el){
34997         var td = this.tree.getTreeEl().dom, nd = el.dom;
34998         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34999             td.scrollLeft = nd.offsetLeft;
35000         }
35001         var w = Math.min(
35002                 this.maxWidth,
35003                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35004         this.setSize(w, '');
35005         
35006         return this.fireEvent('beforenodeedit', this, this.editNode);
35007         
35008     },
35009
35010     // private
35011     triggerEdit : function(node){
35012         this.completeEdit();
35013         this.editNode = node;
35014         this.startEdit(node.ui.textNode, node.text);
35015     },
35016
35017     // private
35018     bindScroll : function(){
35019         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35020     },
35021
35022     // private
35023     beforeNodeClick : function(node, e){
35024         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35025         this.lastClick = new Date();
35026         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35027             e.stopEvent();
35028             this.triggerEdit(node);
35029             return false;
35030         }
35031         return true;
35032     },
35033
35034     // private
35035     updateNode : function(ed, value){
35036         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35037         this.editNode.setText(value);
35038     },
35039
35040     // private
35041     onHide : function(){
35042         Roo.tree.TreeEditor.superclass.onHide.call(this);
35043         if(this.editNode){
35044             this.editNode.ui.focus();
35045         }
35046     },
35047
35048     // private
35049     onSpecialKey : function(field, e){
35050         var k = e.getKey();
35051         if(k == e.ESC){
35052             e.stopEvent();
35053             this.cancelEdit();
35054         }else if(k == e.ENTER && !e.hasModifier()){
35055             e.stopEvent();
35056             this.completeEdit();
35057         }
35058     }
35059 });//<Script type="text/javascript">
35060 /*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070  
35071 /**
35072  * Not documented??? - probably should be...
35073  */
35074
35075 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35076     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35077     
35078     renderElements : function(n, a, targetNode, bulkRender){
35079         //consel.log("renderElements?");
35080         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35081
35082         var t = n.getOwnerTree();
35083         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35084         
35085         var cols = t.columns;
35086         var bw = t.borderWidth;
35087         var c = cols[0];
35088         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35089          var cb = typeof a.checked == "boolean";
35090         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35091         var colcls = 'x-t-' + tid + '-c0';
35092         var buf = [
35093             '<li class="x-tree-node">',
35094             
35095                 
35096                 '<div class="x-tree-node-el ', a.cls,'">',
35097                     // extran...
35098                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35099                 
35100                 
35101                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35102                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35103                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35104                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35105                            (a.iconCls ? ' '+a.iconCls : ''),
35106                            '" unselectable="on" />',
35107                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35108                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35109                              
35110                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35111                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35112                             '<span unselectable="on" qtip="' + tx + '">',
35113                              tx,
35114                              '</span></a>' ,
35115                     '</div>',
35116                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35117                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35118                  ];
35119         for(var i = 1, len = cols.length; i < len; i++){
35120             c = cols[i];
35121             colcls = 'x-t-' + tid + '-c' +i;
35122             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35123             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35124                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35125                       "</div>");
35126          }
35127          
35128          buf.push(
35129             '</a>',
35130             '<div class="x-clear"></div></div>',
35131             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35132             "</li>");
35133         
35134         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35135             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35136                                 n.nextSibling.ui.getEl(), buf.join(""));
35137         }else{
35138             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35139         }
35140         var el = this.wrap.firstChild;
35141         this.elRow = el;
35142         this.elNode = el.firstChild;
35143         this.ranchor = el.childNodes[1];
35144         this.ctNode = this.wrap.childNodes[1];
35145         var cs = el.firstChild.childNodes;
35146         this.indentNode = cs[0];
35147         this.ecNode = cs[1];
35148         this.iconNode = cs[2];
35149         var index = 3;
35150         if(cb){
35151             this.checkbox = cs[3];
35152             index++;
35153         }
35154         this.anchor = cs[index];
35155         
35156         this.textNode = cs[index].firstChild;
35157         
35158         //el.on("click", this.onClick, this);
35159         //el.on("dblclick", this.onDblClick, this);
35160         
35161         
35162        // console.log(this);
35163     },
35164     initEvents : function(){
35165         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35166         
35167             
35168         var a = this.ranchor;
35169
35170         var el = Roo.get(a);
35171
35172         if(Roo.isOpera){ // opera render bug ignores the CSS
35173             el.setStyle("text-decoration", "none");
35174         }
35175
35176         el.on("click", this.onClick, this);
35177         el.on("dblclick", this.onDblClick, this);
35178         el.on("contextmenu", this.onContextMenu, this);
35179         
35180     },
35181     
35182     /*onSelectedChange : function(state){
35183         if(state){
35184             this.focus();
35185             this.addClass("x-tree-selected");
35186         }else{
35187             //this.blur();
35188             this.removeClass("x-tree-selected");
35189         }
35190     },*/
35191     addClass : function(cls){
35192         if(this.elRow){
35193             Roo.fly(this.elRow).addClass(cls);
35194         }
35195         
35196     },
35197     
35198     
35199     removeClass : function(cls){
35200         if(this.elRow){
35201             Roo.fly(this.elRow).removeClass(cls);
35202         }
35203     }
35204
35205     
35206     
35207 });//<Script type="text/javascript">
35208
35209 /*
35210  * Based on:
35211  * Ext JS Library 1.1.1
35212  * Copyright(c) 2006-2007, Ext JS, LLC.
35213  *
35214  * Originally Released Under LGPL - original licence link has changed is not relivant.
35215  *
35216  * Fork - LGPL
35217  * <script type="text/javascript">
35218  */
35219  
35220
35221 /**
35222  * @class Roo.tree.ColumnTree
35223  * @extends Roo.data.TreePanel
35224  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35225  * @cfg {int} borderWidth  compined right/left border allowance
35226  * @constructor
35227  * @param {String/HTMLElement/Element} el The container element
35228  * @param {Object} config
35229  */
35230 Roo.tree.ColumnTree =  function(el, config)
35231 {
35232    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35233    this.addEvents({
35234         /**
35235         * @event resize
35236         * Fire this event on a container when it resizes
35237         * @param {int} w Width
35238         * @param {int} h Height
35239         */
35240        "resize" : true
35241     });
35242     this.on('resize', this.onResize, this);
35243 };
35244
35245 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35246     //lines:false,
35247     
35248     
35249     borderWidth: Roo.isBorderBox ? 0 : 2, 
35250     headEls : false,
35251     
35252     render : function(){
35253         // add the header.....
35254        
35255         Roo.tree.ColumnTree.superclass.render.apply(this);
35256         
35257         this.el.addClass('x-column-tree');
35258         
35259         this.headers = this.el.createChild(
35260             {cls:'x-tree-headers'},this.innerCt.dom);
35261    
35262         var cols = this.columns, c;
35263         var totalWidth = 0;
35264         this.headEls = [];
35265         var  len = cols.length;
35266         for(var i = 0; i < len; i++){
35267              c = cols[i];
35268              totalWidth += c.width;
35269             this.headEls.push(this.headers.createChild({
35270                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35271                  cn: {
35272                      cls:'x-tree-hd-text',
35273                      html: c.header
35274                  },
35275                  style:'width:'+(c.width-this.borderWidth)+'px;'
35276              }));
35277         }
35278         this.headers.createChild({cls:'x-clear'});
35279         // prevent floats from wrapping when clipped
35280         this.headers.setWidth(totalWidth);
35281         //this.innerCt.setWidth(totalWidth);
35282         this.innerCt.setStyle({ overflow: 'auto' });
35283         this.onResize(this.width, this.height);
35284              
35285         
35286     },
35287     onResize : function(w,h)
35288     {
35289         this.height = h;
35290         this.width = w;
35291         // resize cols..
35292         this.innerCt.setWidth(this.width);
35293         this.innerCt.setHeight(this.height-20);
35294         
35295         // headers...
35296         var cols = this.columns, c;
35297         var totalWidth = 0;
35298         var expEl = false;
35299         var len = cols.length;
35300         for(var i = 0; i < len; i++){
35301             c = cols[i];
35302             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35303                 // it's the expander..
35304                 expEl  = this.headEls[i];
35305                 continue;
35306             }
35307             totalWidth += c.width;
35308             
35309         }
35310         if (expEl) {
35311             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35312         }
35313         this.headers.setWidth(w-20);
35314
35315         
35316         
35317         
35318     }
35319 });
35320 /*
35321  * Based on:
35322  * Ext JS Library 1.1.1
35323  * Copyright(c) 2006-2007, Ext JS, LLC.
35324  *
35325  * Originally Released Under LGPL - original licence link has changed is not relivant.
35326  *
35327  * Fork - LGPL
35328  * <script type="text/javascript">
35329  */
35330  
35331 /**
35332  * @class Roo.menu.Menu
35333  * @extends Roo.util.Observable
35334  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35335  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35336  * @constructor
35337  * Creates a new Menu
35338  * @param {Object} config Configuration options
35339  */
35340 Roo.menu.Menu = function(config){
35341     Roo.apply(this, config);
35342     this.id = this.id || Roo.id();
35343     this.addEvents({
35344         /**
35345          * @event beforeshow
35346          * Fires before this menu is displayed
35347          * @param {Roo.menu.Menu} this
35348          */
35349         beforeshow : true,
35350         /**
35351          * @event beforehide
35352          * Fires before this menu is hidden
35353          * @param {Roo.menu.Menu} this
35354          */
35355         beforehide : true,
35356         /**
35357          * @event show
35358          * Fires after this menu is displayed
35359          * @param {Roo.menu.Menu} this
35360          */
35361         show : true,
35362         /**
35363          * @event hide
35364          * Fires after this menu is hidden
35365          * @param {Roo.menu.Menu} this
35366          */
35367         hide : true,
35368         /**
35369          * @event click
35370          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35371          * @param {Roo.menu.Menu} this
35372          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35373          * @param {Roo.EventObject} e
35374          */
35375         click : true,
35376         /**
35377          * @event mouseover
35378          * Fires when the mouse is hovering over this menu
35379          * @param {Roo.menu.Menu} this
35380          * @param {Roo.EventObject} e
35381          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35382          */
35383         mouseover : true,
35384         /**
35385          * @event mouseout
35386          * Fires when the mouse exits this menu
35387          * @param {Roo.menu.Menu} this
35388          * @param {Roo.EventObject} e
35389          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35390          */
35391         mouseout : true,
35392         /**
35393          * @event itemclick
35394          * Fires when a menu item contained in this menu is clicked
35395          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35396          * @param {Roo.EventObject} e
35397          */
35398         itemclick: true
35399     });
35400     if (this.registerMenu) {
35401         Roo.menu.MenuMgr.register(this);
35402     }
35403     
35404     var mis = this.items;
35405     this.items = new Roo.util.MixedCollection();
35406     if(mis){
35407         this.add.apply(this, mis);
35408     }
35409 };
35410
35411 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35412     /**
35413      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35414      */
35415     minWidth : 120,
35416     /**
35417      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35418      * for bottom-right shadow (defaults to "sides")
35419      */
35420     shadow : "sides",
35421     /**
35422      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35423      * this menu (defaults to "tl-tr?")
35424      */
35425     subMenuAlign : "tl-tr?",
35426     /**
35427      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35428      * relative to its element of origin (defaults to "tl-bl?")
35429      */
35430     defaultAlign : "tl-bl?",
35431     /**
35432      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35433      */
35434     allowOtherMenus : false,
35435     /**
35436      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35437      */
35438     registerMenu : true,
35439
35440     hidden:true,
35441
35442     // private
35443     render : function(){
35444         if(this.el){
35445             return;
35446         }
35447         var el = this.el = new Roo.Layer({
35448             cls: "x-menu",
35449             shadow:this.shadow,
35450             constrain: false,
35451             parentEl: this.parentEl || document.body,
35452             zindex:15000
35453         });
35454
35455         this.keyNav = new Roo.menu.MenuNav(this);
35456
35457         if(this.plain){
35458             el.addClass("x-menu-plain");
35459         }
35460         if(this.cls){
35461             el.addClass(this.cls);
35462         }
35463         // generic focus element
35464         this.focusEl = el.createChild({
35465             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35466         });
35467         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35468         ul.on("click", this.onClick, this);
35469         ul.on("mouseover", this.onMouseOver, this);
35470         ul.on("mouseout", this.onMouseOut, this);
35471         this.items.each(function(item){
35472             if (item.hidden) {
35473                 return;
35474             }
35475             
35476             var li = document.createElement("li");
35477             li.className = "x-menu-list-item";
35478             ul.dom.appendChild(li);
35479             item.render(li, this);
35480         }, this);
35481         this.ul = ul;
35482         this.autoWidth();
35483     },
35484
35485     // private
35486     autoWidth : function(){
35487         var el = this.el, ul = this.ul;
35488         if(!el){
35489             return;
35490         }
35491         var w = this.width;
35492         if(w){
35493             el.setWidth(w);
35494         }else if(Roo.isIE){
35495             el.setWidth(this.minWidth);
35496             var t = el.dom.offsetWidth; // force recalc
35497             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35498         }
35499     },
35500
35501     // private
35502     delayAutoWidth : function(){
35503         if(this.rendered){
35504             if(!this.awTask){
35505                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35506             }
35507             this.awTask.delay(20);
35508         }
35509     },
35510
35511     // private
35512     findTargetItem : function(e){
35513         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35514         if(t && t.menuItemId){
35515             return this.items.get(t.menuItemId);
35516         }
35517     },
35518
35519     // private
35520     onClick : function(e){
35521         var t;
35522         if(t = this.findTargetItem(e)){
35523             t.onClick(e);
35524             this.fireEvent("click", this, t, e);
35525         }
35526     },
35527
35528     // private
35529     setActiveItem : function(item, autoExpand){
35530         if(item != this.activeItem){
35531             if(this.activeItem){
35532                 this.activeItem.deactivate();
35533             }
35534             this.activeItem = item;
35535             item.activate(autoExpand);
35536         }else if(autoExpand){
35537             item.expandMenu();
35538         }
35539     },
35540
35541     // private
35542     tryActivate : function(start, step){
35543         var items = this.items;
35544         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35545             var item = items.get(i);
35546             if(!item.disabled && item.canActivate){
35547                 this.setActiveItem(item, false);
35548                 return item;
35549             }
35550         }
35551         return false;
35552     },
35553
35554     // private
35555     onMouseOver : function(e){
35556         var t;
35557         if(t = this.findTargetItem(e)){
35558             if(t.canActivate && !t.disabled){
35559                 this.setActiveItem(t, true);
35560             }
35561         }
35562         this.fireEvent("mouseover", this, e, t);
35563     },
35564
35565     // private
35566     onMouseOut : function(e){
35567         var t;
35568         if(t = this.findTargetItem(e)){
35569             if(t == this.activeItem && t.shouldDeactivate(e)){
35570                 this.activeItem.deactivate();
35571                 delete this.activeItem;
35572             }
35573         }
35574         this.fireEvent("mouseout", this, e, t);
35575     },
35576
35577     /**
35578      * Read-only.  Returns true if the menu is currently displayed, else false.
35579      * @type Boolean
35580      */
35581     isVisible : function(){
35582         return this.el && !this.hidden;
35583     },
35584
35585     /**
35586      * Displays this menu relative to another element
35587      * @param {String/HTMLElement/Roo.Element} element The element to align to
35588      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35589      * the element (defaults to this.defaultAlign)
35590      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35591      */
35592     show : function(el, pos, parentMenu){
35593         this.parentMenu = parentMenu;
35594         if(!this.el){
35595             this.render();
35596         }
35597         this.fireEvent("beforeshow", this);
35598         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35599     },
35600
35601     /**
35602      * Displays this menu at a specific xy position
35603      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35604      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35605      */
35606     showAt : function(xy, parentMenu, /* private: */_e){
35607         this.parentMenu = parentMenu;
35608         if(!this.el){
35609             this.render();
35610         }
35611         if(_e !== false){
35612             this.fireEvent("beforeshow", this);
35613             xy = this.el.adjustForConstraints(xy);
35614         }
35615         this.el.setXY(xy);
35616         this.el.show();
35617         this.hidden = false;
35618         this.focus();
35619         this.fireEvent("show", this);
35620     },
35621
35622     focus : function(){
35623         if(!this.hidden){
35624             this.doFocus.defer(50, this);
35625         }
35626     },
35627
35628     doFocus : function(){
35629         if(!this.hidden){
35630             this.focusEl.focus();
35631         }
35632     },
35633
35634     /**
35635      * Hides this menu and optionally all parent menus
35636      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35637      */
35638     hide : function(deep){
35639         if(this.el && this.isVisible()){
35640             this.fireEvent("beforehide", this);
35641             if(this.activeItem){
35642                 this.activeItem.deactivate();
35643                 this.activeItem = null;
35644             }
35645             this.el.hide();
35646             this.hidden = true;
35647             this.fireEvent("hide", this);
35648         }
35649         if(deep === true && this.parentMenu){
35650             this.parentMenu.hide(true);
35651         }
35652     },
35653
35654     /**
35655      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35656      * Any of the following are valid:
35657      * <ul>
35658      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35659      * <li>An HTMLElement object which will be converted to a menu item</li>
35660      * <li>A menu item config object that will be created as a new menu item</li>
35661      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35662      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35663      * </ul>
35664      * Usage:
35665      * <pre><code>
35666 // Create the menu
35667 var menu = new Roo.menu.Menu();
35668
35669 // Create a menu item to add by reference
35670 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35671
35672 // Add a bunch of items at once using different methods.
35673 // Only the last item added will be returned.
35674 var item = menu.add(
35675     menuItem,                // add existing item by ref
35676     'Dynamic Item',          // new TextItem
35677     '-',                     // new separator
35678     { text: 'Config Item' }  // new item by config
35679 );
35680 </code></pre>
35681      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35682      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35683      */
35684     add : function(){
35685         var a = arguments, l = a.length, item;
35686         for(var i = 0; i < l; i++){
35687             var el = a[i];
35688             if ((typeof(el) == "object") && el.xtype && el.xns) {
35689                 el = Roo.factory(el, Roo.menu);
35690             }
35691             
35692             if(el.render){ // some kind of Item
35693                 item = this.addItem(el);
35694             }else if(typeof el == "string"){ // string
35695                 if(el == "separator" || el == "-"){
35696                     item = this.addSeparator();
35697                 }else{
35698                     item = this.addText(el);
35699                 }
35700             }else if(el.tagName || el.el){ // element
35701                 item = this.addElement(el);
35702             }else if(typeof el == "object"){ // must be menu item config?
35703                 item = this.addMenuItem(el);
35704             }
35705         }
35706         return item;
35707     },
35708
35709     /**
35710      * Returns this menu's underlying {@link Roo.Element} object
35711      * @return {Roo.Element} The element
35712      */
35713     getEl : function(){
35714         if(!this.el){
35715             this.render();
35716         }
35717         return this.el;
35718     },
35719
35720     /**
35721      * Adds a separator bar to the menu
35722      * @return {Roo.menu.Item} The menu item that was added
35723      */
35724     addSeparator : function(){
35725         return this.addItem(new Roo.menu.Separator());
35726     },
35727
35728     /**
35729      * Adds an {@link Roo.Element} object to the menu
35730      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35731      * @return {Roo.menu.Item} The menu item that was added
35732      */
35733     addElement : function(el){
35734         return this.addItem(new Roo.menu.BaseItem(el));
35735     },
35736
35737     /**
35738      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35739      * @param {Roo.menu.Item} item The menu item to add
35740      * @return {Roo.menu.Item} The menu item that was added
35741      */
35742     addItem : function(item){
35743         this.items.add(item);
35744         if(this.ul){
35745             var li = document.createElement("li");
35746             li.className = "x-menu-list-item";
35747             this.ul.dom.appendChild(li);
35748             item.render(li, this);
35749             this.delayAutoWidth();
35750         }
35751         return item;
35752     },
35753
35754     /**
35755      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35756      * @param {Object} config A MenuItem config object
35757      * @return {Roo.menu.Item} The menu item that was added
35758      */
35759     addMenuItem : function(config){
35760         if(!(config instanceof Roo.menu.Item)){
35761             if(typeof config.checked == "boolean"){ // must be check menu item config?
35762                 config = new Roo.menu.CheckItem(config);
35763             }else{
35764                 config = new Roo.menu.Item(config);
35765             }
35766         }
35767         return this.addItem(config);
35768     },
35769
35770     /**
35771      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35772      * @param {String} text The text to display in the menu item
35773      * @return {Roo.menu.Item} The menu item that was added
35774      */
35775     addText : function(text){
35776         return this.addItem(new Roo.menu.TextItem({ text : text }));
35777     },
35778
35779     /**
35780      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35781      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35782      * @param {Roo.menu.Item} item The menu item to add
35783      * @return {Roo.menu.Item} The menu item that was added
35784      */
35785     insert : function(index, item){
35786         this.items.insert(index, item);
35787         if(this.ul){
35788             var li = document.createElement("li");
35789             li.className = "x-menu-list-item";
35790             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35791             item.render(li, this);
35792             this.delayAutoWidth();
35793         }
35794         return item;
35795     },
35796
35797     /**
35798      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35799      * @param {Roo.menu.Item} item The menu item to remove
35800      */
35801     remove : function(item){
35802         this.items.removeKey(item.id);
35803         item.destroy();
35804     },
35805
35806     /**
35807      * Removes and destroys all items in the menu
35808      */
35809     removeAll : function(){
35810         var f;
35811         while(f = this.items.first()){
35812             this.remove(f);
35813         }
35814     }
35815 });
35816
35817 // MenuNav is a private utility class used internally by the Menu
35818 Roo.menu.MenuNav = function(menu){
35819     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35820     this.scope = this.menu = menu;
35821 };
35822
35823 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35824     doRelay : function(e, h){
35825         var k = e.getKey();
35826         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35827             this.menu.tryActivate(0, 1);
35828             return false;
35829         }
35830         return h.call(this.scope || this, e, this.menu);
35831     },
35832
35833     up : function(e, m){
35834         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35835             m.tryActivate(m.items.length-1, -1);
35836         }
35837     },
35838
35839     down : function(e, m){
35840         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35841             m.tryActivate(0, 1);
35842         }
35843     },
35844
35845     right : function(e, m){
35846         if(m.activeItem){
35847             m.activeItem.expandMenu(true);
35848         }
35849     },
35850
35851     left : function(e, m){
35852         m.hide();
35853         if(m.parentMenu && m.parentMenu.activeItem){
35854             m.parentMenu.activeItem.activate();
35855         }
35856     },
35857
35858     enter : function(e, m){
35859         if(m.activeItem){
35860             e.stopPropagation();
35861             m.activeItem.onClick(e);
35862             m.fireEvent("click", this, m.activeItem);
35863             return true;
35864         }
35865     }
35866 });/*
35867  * Based on:
35868  * Ext JS Library 1.1.1
35869  * Copyright(c) 2006-2007, Ext JS, LLC.
35870  *
35871  * Originally Released Under LGPL - original licence link has changed is not relivant.
35872  *
35873  * Fork - LGPL
35874  * <script type="text/javascript">
35875  */
35876  
35877 /**
35878  * @class Roo.menu.MenuMgr
35879  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35880  * @singleton
35881  */
35882 Roo.menu.MenuMgr = function(){
35883    var menus, active, groups = {}, attached = false, lastShow = new Date();
35884
35885    // private - called when first menu is created
35886    function init(){
35887        menus = {};
35888        active = new Roo.util.MixedCollection();
35889        Roo.get(document).addKeyListener(27, function(){
35890            if(active.length > 0){
35891                hideAll();
35892            }
35893        });
35894    }
35895
35896    // private
35897    function hideAll(){
35898        if(active && active.length > 0){
35899            var c = active.clone();
35900            c.each(function(m){
35901                m.hide();
35902            });
35903        }
35904    }
35905
35906    // private
35907    function onHide(m){
35908        active.remove(m);
35909        if(active.length < 1){
35910            Roo.get(document).un("mousedown", onMouseDown);
35911            attached = false;
35912        }
35913    }
35914
35915    // private
35916    function onShow(m){
35917        var last = active.last();
35918        lastShow = new Date();
35919        active.add(m);
35920        if(!attached){
35921            Roo.get(document).on("mousedown", onMouseDown);
35922            attached = true;
35923        }
35924        if(m.parentMenu){
35925           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35926           m.parentMenu.activeChild = m;
35927        }else if(last && last.isVisible()){
35928           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35929        }
35930    }
35931
35932    // private
35933    function onBeforeHide(m){
35934        if(m.activeChild){
35935            m.activeChild.hide();
35936        }
35937        if(m.autoHideTimer){
35938            clearTimeout(m.autoHideTimer);
35939            delete m.autoHideTimer;
35940        }
35941    }
35942
35943    // private
35944    function onBeforeShow(m){
35945        var pm = m.parentMenu;
35946        if(!pm && !m.allowOtherMenus){
35947            hideAll();
35948        }else if(pm && pm.activeChild && active != m){
35949            pm.activeChild.hide();
35950        }
35951    }
35952
35953    // private
35954    function onMouseDown(e){
35955        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35956            hideAll();
35957        }
35958    }
35959
35960    // private
35961    function onBeforeCheck(mi, state){
35962        if(state){
35963            var g = groups[mi.group];
35964            for(var i = 0, l = g.length; i < l; i++){
35965                if(g[i] != mi){
35966                    g[i].setChecked(false);
35967                }
35968            }
35969        }
35970    }
35971
35972    return {
35973
35974        /**
35975         * Hides all menus that are currently visible
35976         */
35977        hideAll : function(){
35978             hideAll();  
35979        },
35980
35981        // private
35982        register : function(menu){
35983            if(!menus){
35984                init();
35985            }
35986            menus[menu.id] = menu;
35987            menu.on("beforehide", onBeforeHide);
35988            menu.on("hide", onHide);
35989            menu.on("beforeshow", onBeforeShow);
35990            menu.on("show", onShow);
35991            var g = menu.group;
35992            if(g && menu.events["checkchange"]){
35993                if(!groups[g]){
35994                    groups[g] = [];
35995                }
35996                groups[g].push(menu);
35997                menu.on("checkchange", onCheck);
35998            }
35999        },
36000
36001         /**
36002          * Returns a {@link Roo.menu.Menu} object
36003          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36004          * be used to generate and return a new Menu instance.
36005          */
36006        get : function(menu){
36007            if(typeof menu == "string"){ // menu id
36008                return menus[menu];
36009            }else if(menu.events){  // menu instance
36010                return menu;
36011            }else if(typeof menu.length == 'number'){ // array of menu items?
36012                return new Roo.menu.Menu({items:menu});
36013            }else{ // otherwise, must be a config
36014                return new Roo.menu.Menu(menu);
36015            }
36016        },
36017
36018        // private
36019        unregister : function(menu){
36020            delete menus[menu.id];
36021            menu.un("beforehide", onBeforeHide);
36022            menu.un("hide", onHide);
36023            menu.un("beforeshow", onBeforeShow);
36024            menu.un("show", onShow);
36025            var g = menu.group;
36026            if(g && menu.events["checkchange"]){
36027                groups[g].remove(menu);
36028                menu.un("checkchange", onCheck);
36029            }
36030        },
36031
36032        // private
36033        registerCheckable : function(menuItem){
36034            var g = menuItem.group;
36035            if(g){
36036                if(!groups[g]){
36037                    groups[g] = [];
36038                }
36039                groups[g].push(menuItem);
36040                menuItem.on("beforecheckchange", onBeforeCheck);
36041            }
36042        },
36043
36044        // private
36045        unregisterCheckable : function(menuItem){
36046            var g = menuItem.group;
36047            if(g){
36048                groups[g].remove(menuItem);
36049                menuItem.un("beforecheckchange", onBeforeCheck);
36050            }
36051        }
36052    };
36053 }();/*
36054  * Based on:
36055  * Ext JS Library 1.1.1
36056  * Copyright(c) 2006-2007, Ext JS, LLC.
36057  *
36058  * Originally Released Under LGPL - original licence link has changed is not relivant.
36059  *
36060  * Fork - LGPL
36061  * <script type="text/javascript">
36062  */
36063  
36064
36065 /**
36066  * @class Roo.menu.BaseItem
36067  * @extends Roo.Component
36068  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36069  * management and base configuration options shared by all menu components.
36070  * @constructor
36071  * Creates a new BaseItem
36072  * @param {Object} config Configuration options
36073  */
36074 Roo.menu.BaseItem = function(config){
36075     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36076
36077     this.addEvents({
36078         /**
36079          * @event click
36080          * Fires when this item is clicked
36081          * @param {Roo.menu.BaseItem} this
36082          * @param {Roo.EventObject} e
36083          */
36084         click: true,
36085         /**
36086          * @event activate
36087          * Fires when this item is activated
36088          * @param {Roo.menu.BaseItem} this
36089          */
36090         activate : true,
36091         /**
36092          * @event deactivate
36093          * Fires when this item is deactivated
36094          * @param {Roo.menu.BaseItem} this
36095          */
36096         deactivate : true
36097     });
36098
36099     if(this.handler){
36100         this.on("click", this.handler, this.scope, true);
36101     }
36102 };
36103
36104 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36105     /**
36106      * @cfg {Function} handler
36107      * A function that will handle the click event of this menu item (defaults to undefined)
36108      */
36109     /**
36110      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36111      */
36112     canActivate : false,
36113     
36114      /**
36115      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36116      */
36117     hidden: false,
36118     
36119     /**
36120      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36121      */
36122     activeClass : "x-menu-item-active",
36123     /**
36124      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36125      */
36126     hideOnClick : true,
36127     /**
36128      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36129      */
36130     hideDelay : 100,
36131
36132     // private
36133     ctype: "Roo.menu.BaseItem",
36134
36135     // private
36136     actionMode : "container",
36137
36138     // private
36139     render : function(container, parentMenu){
36140         this.parentMenu = parentMenu;
36141         Roo.menu.BaseItem.superclass.render.call(this, container);
36142         this.container.menuItemId = this.id;
36143     },
36144
36145     // private
36146     onRender : function(container, position){
36147         this.el = Roo.get(this.el);
36148         container.dom.appendChild(this.el.dom);
36149     },
36150
36151     // private
36152     onClick : function(e){
36153         if(!this.disabled && this.fireEvent("click", this, e) !== false
36154                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36155             this.handleClick(e);
36156         }else{
36157             e.stopEvent();
36158         }
36159     },
36160
36161     // private
36162     activate : function(){
36163         if(this.disabled){
36164             return false;
36165         }
36166         var li = this.container;
36167         li.addClass(this.activeClass);
36168         this.region = li.getRegion().adjust(2, 2, -2, -2);
36169         this.fireEvent("activate", this);
36170         return true;
36171     },
36172
36173     // private
36174     deactivate : function(){
36175         this.container.removeClass(this.activeClass);
36176         this.fireEvent("deactivate", this);
36177     },
36178
36179     // private
36180     shouldDeactivate : function(e){
36181         return !this.region || !this.region.contains(e.getPoint());
36182     },
36183
36184     // private
36185     handleClick : function(e){
36186         if(this.hideOnClick){
36187             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36188         }
36189     },
36190
36191     // private
36192     expandMenu : function(autoActivate){
36193         // do nothing
36194     },
36195
36196     // private
36197     hideMenu : function(){
36198         // do nothing
36199     }
36200 });/*
36201  * Based on:
36202  * Ext JS Library 1.1.1
36203  * Copyright(c) 2006-2007, Ext JS, LLC.
36204  *
36205  * Originally Released Under LGPL - original licence link has changed is not relivant.
36206  *
36207  * Fork - LGPL
36208  * <script type="text/javascript">
36209  */
36210  
36211 /**
36212  * @class Roo.menu.Adapter
36213  * @extends Roo.menu.BaseItem
36214  * 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.
36215  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36216  * @constructor
36217  * Creates a new Adapter
36218  * @param {Object} config Configuration options
36219  */
36220 Roo.menu.Adapter = function(component, config){
36221     Roo.menu.Adapter.superclass.constructor.call(this, config);
36222     this.component = component;
36223 };
36224 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36225     // private
36226     canActivate : true,
36227
36228     // private
36229     onRender : function(container, position){
36230         this.component.render(container);
36231         this.el = this.component.getEl();
36232     },
36233
36234     // private
36235     activate : function(){
36236         if(this.disabled){
36237             return false;
36238         }
36239         this.component.focus();
36240         this.fireEvent("activate", this);
36241         return true;
36242     },
36243
36244     // private
36245     deactivate : function(){
36246         this.fireEvent("deactivate", this);
36247     },
36248
36249     // private
36250     disable : function(){
36251         this.component.disable();
36252         Roo.menu.Adapter.superclass.disable.call(this);
36253     },
36254
36255     // private
36256     enable : function(){
36257         this.component.enable();
36258         Roo.menu.Adapter.superclass.enable.call(this);
36259     }
36260 });/*
36261  * Based on:
36262  * Ext JS Library 1.1.1
36263  * Copyright(c) 2006-2007, Ext JS, LLC.
36264  *
36265  * Originally Released Under LGPL - original licence link has changed is not relivant.
36266  *
36267  * Fork - LGPL
36268  * <script type="text/javascript">
36269  */
36270
36271 /**
36272  * @class Roo.menu.TextItem
36273  * @extends Roo.menu.BaseItem
36274  * Adds a static text string to a menu, usually used as either a heading or group separator.
36275  * Note: old style constructor with text is still supported.
36276  * 
36277  * @constructor
36278  * Creates a new TextItem
36279  * @param {Object} cfg Configuration
36280  */
36281 Roo.menu.TextItem = function(cfg){
36282     if (typeof(cfg) == 'string') {
36283         this.text = cfg;
36284     } else {
36285         Roo.apply(this,cfg);
36286     }
36287     
36288     Roo.menu.TextItem.superclass.constructor.call(this);
36289 };
36290
36291 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36292     /**
36293      * @cfg {Boolean} text Text to show on item.
36294      */
36295     text : '',
36296     
36297     /**
36298      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36299      */
36300     hideOnClick : false,
36301     /**
36302      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36303      */
36304     itemCls : "x-menu-text",
36305
36306     // private
36307     onRender : function(){
36308         var s = document.createElement("span");
36309         s.className = this.itemCls;
36310         s.innerHTML = this.text;
36311         this.el = s;
36312         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36313     }
36314 });/*
36315  * Based on:
36316  * Ext JS Library 1.1.1
36317  * Copyright(c) 2006-2007, Ext JS, LLC.
36318  *
36319  * Originally Released Under LGPL - original licence link has changed is not relivant.
36320  *
36321  * Fork - LGPL
36322  * <script type="text/javascript">
36323  */
36324
36325 /**
36326  * @class Roo.menu.Separator
36327  * @extends Roo.menu.BaseItem
36328  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36329  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36330  * @constructor
36331  * @param {Object} config Configuration options
36332  */
36333 Roo.menu.Separator = function(config){
36334     Roo.menu.Separator.superclass.constructor.call(this, config);
36335 };
36336
36337 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36338     /**
36339      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36340      */
36341     itemCls : "x-menu-sep",
36342     /**
36343      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36344      */
36345     hideOnClick : false,
36346
36347     // private
36348     onRender : function(li){
36349         var s = document.createElement("span");
36350         s.className = this.itemCls;
36351         s.innerHTML = "&#160;";
36352         this.el = s;
36353         li.addClass("x-menu-sep-li");
36354         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36355     }
36356 });/*
36357  * Based on:
36358  * Ext JS Library 1.1.1
36359  * Copyright(c) 2006-2007, Ext JS, LLC.
36360  *
36361  * Originally Released Under LGPL - original licence link has changed is not relivant.
36362  *
36363  * Fork - LGPL
36364  * <script type="text/javascript">
36365  */
36366 /**
36367  * @class Roo.menu.Item
36368  * @extends Roo.menu.BaseItem
36369  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36370  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36371  * activation and click handling.
36372  * @constructor
36373  * Creates a new Item
36374  * @param {Object} config Configuration options
36375  */
36376 Roo.menu.Item = function(config){
36377     Roo.menu.Item.superclass.constructor.call(this, config);
36378     if(this.menu){
36379         this.menu = Roo.menu.MenuMgr.get(this.menu);
36380     }
36381 };
36382 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36383     
36384     /**
36385      * @cfg {String} text
36386      * The text to show on the menu item.
36387      */
36388     text: '',
36389      /**
36390      * @cfg {String} HTML to render in menu
36391      * The text to show on the menu item (HTML version).
36392      */
36393     html: '',
36394     /**
36395      * @cfg {String} icon
36396      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36397      */
36398     icon: undefined,
36399     /**
36400      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36401      */
36402     itemCls : "x-menu-item",
36403     /**
36404      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36405      */
36406     canActivate : true,
36407     /**
36408      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36409      */
36410     showDelay: 200,
36411     // doc'd in BaseItem
36412     hideDelay: 200,
36413
36414     // private
36415     ctype: "Roo.menu.Item",
36416     
36417     // private
36418     onRender : function(container, position){
36419         var el = document.createElement("a");
36420         el.hideFocus = true;
36421         el.unselectable = "on";
36422         el.href = this.href || "#";
36423         if(this.hrefTarget){
36424             el.target = this.hrefTarget;
36425         }
36426         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36427         
36428         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36429         
36430         el.innerHTML = String.format(
36431                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36432                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36433         this.el = el;
36434         Roo.menu.Item.superclass.onRender.call(this, container, position);
36435     },
36436
36437     /**
36438      * Sets the text to display in this menu item
36439      * @param {String} text The text to display
36440      * @param {Boolean} isHTML true to indicate text is pure html.
36441      */
36442     setText : function(text, isHTML){
36443         if (isHTML) {
36444             this.html = text;
36445         } else {
36446             this.text = text;
36447             this.html = '';
36448         }
36449         if(this.rendered){
36450             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36451      
36452             this.el.update(String.format(
36453                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36454                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36455             this.parentMenu.autoWidth();
36456         }
36457     },
36458
36459     // private
36460     handleClick : function(e){
36461         if(!this.href){ // if no link defined, stop the event automatically
36462             e.stopEvent();
36463         }
36464         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36465     },
36466
36467     // private
36468     activate : function(autoExpand){
36469         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36470             this.focus();
36471             if(autoExpand){
36472                 this.expandMenu();
36473             }
36474         }
36475         return true;
36476     },
36477
36478     // private
36479     shouldDeactivate : function(e){
36480         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36481             if(this.menu && this.menu.isVisible()){
36482                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36483             }
36484             return true;
36485         }
36486         return false;
36487     },
36488
36489     // private
36490     deactivate : function(){
36491         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36492         this.hideMenu();
36493     },
36494
36495     // private
36496     expandMenu : function(autoActivate){
36497         if(!this.disabled && this.menu){
36498             clearTimeout(this.hideTimer);
36499             delete this.hideTimer;
36500             if(!this.menu.isVisible() && !this.showTimer){
36501                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36502             }else if (this.menu.isVisible() && autoActivate){
36503                 this.menu.tryActivate(0, 1);
36504             }
36505         }
36506     },
36507
36508     // private
36509     deferExpand : function(autoActivate){
36510         delete this.showTimer;
36511         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36512         if(autoActivate){
36513             this.menu.tryActivate(0, 1);
36514         }
36515     },
36516
36517     // private
36518     hideMenu : function(){
36519         clearTimeout(this.showTimer);
36520         delete this.showTimer;
36521         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36522             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36523         }
36524     },
36525
36526     // private
36527     deferHide : function(){
36528         delete this.hideTimer;
36529         this.menu.hide();
36530     }
36531 });/*
36532  * Based on:
36533  * Ext JS Library 1.1.1
36534  * Copyright(c) 2006-2007, Ext JS, LLC.
36535  *
36536  * Originally Released Under LGPL - original licence link has changed is not relivant.
36537  *
36538  * Fork - LGPL
36539  * <script type="text/javascript">
36540  */
36541  
36542 /**
36543  * @class Roo.menu.CheckItem
36544  * @extends Roo.menu.Item
36545  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36546  * @constructor
36547  * Creates a new CheckItem
36548  * @param {Object} config Configuration options
36549  */
36550 Roo.menu.CheckItem = function(config){
36551     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36552     this.addEvents({
36553         /**
36554          * @event beforecheckchange
36555          * Fires before the checked value is set, providing an opportunity to cancel if needed
36556          * @param {Roo.menu.CheckItem} this
36557          * @param {Boolean} checked The new checked value that will be set
36558          */
36559         "beforecheckchange" : true,
36560         /**
36561          * @event checkchange
36562          * Fires after the checked value has been set
36563          * @param {Roo.menu.CheckItem} this
36564          * @param {Boolean} checked The checked value that was set
36565          */
36566         "checkchange" : true
36567     });
36568     if(this.checkHandler){
36569         this.on('checkchange', this.checkHandler, this.scope);
36570     }
36571 };
36572 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36573     /**
36574      * @cfg {String} group
36575      * All check items with the same group name will automatically be grouped into a single-select
36576      * radio button group (defaults to '')
36577      */
36578     /**
36579      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36580      */
36581     itemCls : "x-menu-item x-menu-check-item",
36582     /**
36583      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36584      */
36585     groupClass : "x-menu-group-item",
36586
36587     /**
36588      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36589      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36590      * initialized with checked = true will be rendered as checked.
36591      */
36592     checked: false,
36593
36594     // private
36595     ctype: "Roo.menu.CheckItem",
36596
36597     // private
36598     onRender : function(c){
36599         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36600         if(this.group){
36601             this.el.addClass(this.groupClass);
36602         }
36603         Roo.menu.MenuMgr.registerCheckable(this);
36604         if(this.checked){
36605             this.checked = false;
36606             this.setChecked(true, true);
36607         }
36608     },
36609
36610     // private
36611     destroy : function(){
36612         if(this.rendered){
36613             Roo.menu.MenuMgr.unregisterCheckable(this);
36614         }
36615         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36616     },
36617
36618     /**
36619      * Set the checked state of this item
36620      * @param {Boolean} checked The new checked value
36621      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36622      */
36623     setChecked : function(state, suppressEvent){
36624         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36625             if(this.container){
36626                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36627             }
36628             this.checked = state;
36629             if(suppressEvent !== true){
36630                 this.fireEvent("checkchange", this, state);
36631             }
36632         }
36633     },
36634
36635     // private
36636     handleClick : function(e){
36637        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36638            this.setChecked(!this.checked);
36639        }
36640        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36641     }
36642 });/*
36643  * Based on:
36644  * Ext JS Library 1.1.1
36645  * Copyright(c) 2006-2007, Ext JS, LLC.
36646  *
36647  * Originally Released Under LGPL - original licence link has changed is not relivant.
36648  *
36649  * Fork - LGPL
36650  * <script type="text/javascript">
36651  */
36652  
36653 /**
36654  * @class Roo.menu.DateItem
36655  * @extends Roo.menu.Adapter
36656  * A menu item that wraps the {@link Roo.DatPicker} component.
36657  * @constructor
36658  * Creates a new DateItem
36659  * @param {Object} config Configuration options
36660  */
36661 Roo.menu.DateItem = function(config){
36662     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36663     /** The Roo.DatePicker object @type Roo.DatePicker */
36664     this.picker = this.component;
36665     this.addEvents({select: true});
36666     
36667     this.picker.on("render", function(picker){
36668         picker.getEl().swallowEvent("click");
36669         picker.container.addClass("x-menu-date-item");
36670     });
36671
36672     this.picker.on("select", this.onSelect, this);
36673 };
36674
36675 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36676     // private
36677     onSelect : function(picker, date){
36678         this.fireEvent("select", this, date, picker);
36679         Roo.menu.DateItem.superclass.handleClick.call(this);
36680     }
36681 });/*
36682  * Based on:
36683  * Ext JS Library 1.1.1
36684  * Copyright(c) 2006-2007, Ext JS, LLC.
36685  *
36686  * Originally Released Under LGPL - original licence link has changed is not relivant.
36687  *
36688  * Fork - LGPL
36689  * <script type="text/javascript">
36690  */
36691  
36692 /**
36693  * @class Roo.menu.ColorItem
36694  * @extends Roo.menu.Adapter
36695  * A menu item that wraps the {@link Roo.ColorPalette} component.
36696  * @constructor
36697  * Creates a new ColorItem
36698  * @param {Object} config Configuration options
36699  */
36700 Roo.menu.ColorItem = function(config){
36701     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36702     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36703     this.palette = this.component;
36704     this.relayEvents(this.palette, ["select"]);
36705     if(this.selectHandler){
36706         this.on('select', this.selectHandler, this.scope);
36707     }
36708 };
36709 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36710  * Based on:
36711  * Ext JS Library 1.1.1
36712  * Copyright(c) 2006-2007, Ext JS, LLC.
36713  *
36714  * Originally Released Under LGPL - original licence link has changed is not relivant.
36715  *
36716  * Fork - LGPL
36717  * <script type="text/javascript">
36718  */
36719  
36720
36721 /**
36722  * @class Roo.menu.DateMenu
36723  * @extends Roo.menu.Menu
36724  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36725  * @constructor
36726  * Creates a new DateMenu
36727  * @param {Object} config Configuration options
36728  */
36729 Roo.menu.DateMenu = function(config){
36730     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36731     this.plain = true;
36732     var di = new Roo.menu.DateItem(config);
36733     this.add(di);
36734     /**
36735      * The {@link Roo.DatePicker} instance for this DateMenu
36736      * @type DatePicker
36737      */
36738     this.picker = di.picker;
36739     /**
36740      * @event select
36741      * @param {DatePicker} picker
36742      * @param {Date} date
36743      */
36744     this.relayEvents(di, ["select"]);
36745     this.on('beforeshow', function(){
36746         if(this.picker){
36747             this.picker.hideMonthPicker(false);
36748         }
36749     }, this);
36750 };
36751 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36752     cls:'x-date-menu'
36753 });/*
36754  * Based on:
36755  * Ext JS Library 1.1.1
36756  * Copyright(c) 2006-2007, Ext JS, LLC.
36757  *
36758  * Originally Released Under LGPL - original licence link has changed is not relivant.
36759  *
36760  * Fork - LGPL
36761  * <script type="text/javascript">
36762  */
36763  
36764
36765 /**
36766  * @class Roo.menu.ColorMenu
36767  * @extends Roo.menu.Menu
36768  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36769  * @constructor
36770  * Creates a new ColorMenu
36771  * @param {Object} config Configuration options
36772  */
36773 Roo.menu.ColorMenu = function(config){
36774     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36775     this.plain = true;
36776     var ci = new Roo.menu.ColorItem(config);
36777     this.add(ci);
36778     /**
36779      * The {@link Roo.ColorPalette} instance for this ColorMenu
36780      * @type ColorPalette
36781      */
36782     this.palette = ci.palette;
36783     /**
36784      * @event select
36785      * @param {ColorPalette} palette
36786      * @param {String} color
36787      */
36788     this.relayEvents(ci, ["select"]);
36789 };
36790 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36791  * Based on:
36792  * Ext JS Library 1.1.1
36793  * Copyright(c) 2006-2007, Ext JS, LLC.
36794  *
36795  * Originally Released Under LGPL - original licence link has changed is not relivant.
36796  *
36797  * Fork - LGPL
36798  * <script type="text/javascript">
36799  */
36800  
36801 /**
36802  * @class Roo.form.Field
36803  * @extends Roo.BoxComponent
36804  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36805  * @constructor
36806  * Creates a new Field
36807  * @param {Object} config Configuration options
36808  */
36809 Roo.form.Field = function(config){
36810     Roo.form.Field.superclass.constructor.call(this, config);
36811 };
36812
36813 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36814     /**
36815      * @cfg {String} fieldLabel Label to use when rendering a form.
36816      */
36817        /**
36818      * @cfg {String} qtip Mouse over tip
36819      */
36820      
36821     /**
36822      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36823      */
36824     invalidClass : "x-form-invalid",
36825     /**
36826      * @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")
36827      */
36828     invalidText : "The value in this field is invalid",
36829     /**
36830      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36831      */
36832     focusClass : "x-form-focus",
36833     /**
36834      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36835       automatic validation (defaults to "keyup").
36836      */
36837     validationEvent : "keyup",
36838     /**
36839      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36840      */
36841     validateOnBlur : true,
36842     /**
36843      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36844      */
36845     validationDelay : 250,
36846     /**
36847      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36848      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36849      */
36850     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36851     /**
36852      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36853      */
36854     fieldClass : "x-form-field",
36855     /**
36856      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36857      *<pre>
36858 Value         Description
36859 -----------   ----------------------------------------------------------------------
36860 qtip          Display a quick tip when the user hovers over the field
36861 title         Display a default browser title attribute popup
36862 under         Add a block div beneath the field containing the error text
36863 side          Add an error icon to the right of the field with a popup on hover
36864 [element id]  Add the error text directly to the innerHTML of the specified element
36865 </pre>
36866      */
36867     msgTarget : 'qtip',
36868     /**
36869      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36870      */
36871     msgFx : 'normal',
36872
36873     /**
36874      * @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.
36875      */
36876     readOnly : false,
36877
36878     /**
36879      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36880      */
36881     disabled : false,
36882
36883     /**
36884      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36885      */
36886     inputType : undefined,
36887     
36888     /**
36889      * @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).
36890          */
36891         tabIndex : undefined,
36892         
36893     // private
36894     isFormField : true,
36895
36896     // private
36897     hasFocus : false,
36898     /**
36899      * @property {Roo.Element} fieldEl
36900      * Element Containing the rendered Field (with label etc.)
36901      */
36902     /**
36903      * @cfg {Mixed} value A value to initialize this field with.
36904      */
36905     value : undefined,
36906
36907     /**
36908      * @cfg {String} name The field's HTML name attribute.
36909      */
36910     /**
36911      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36912      */
36913
36914         // private ??
36915         initComponent : function(){
36916         Roo.form.Field.superclass.initComponent.call(this);
36917         this.addEvents({
36918             /**
36919              * @event focus
36920              * Fires when this field receives input focus.
36921              * @param {Roo.form.Field} this
36922              */
36923             focus : true,
36924             /**
36925              * @event blur
36926              * Fires when this field loses input focus.
36927              * @param {Roo.form.Field} this
36928              */
36929             blur : true,
36930             /**
36931              * @event specialkey
36932              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36933              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36934              * @param {Roo.form.Field} this
36935              * @param {Roo.EventObject} e The event object
36936              */
36937             specialkey : true,
36938             /**
36939              * @event change
36940              * Fires just before the field blurs if the field value has changed.
36941              * @param {Roo.form.Field} this
36942              * @param {Mixed} newValue The new value
36943              * @param {Mixed} oldValue The original value
36944              */
36945             change : true,
36946             /**
36947              * @event invalid
36948              * Fires after the field has been marked as invalid.
36949              * @param {Roo.form.Field} this
36950              * @param {String} msg The validation message
36951              */
36952             invalid : true,
36953             /**
36954              * @event valid
36955              * Fires after the field has been validated with no errors.
36956              * @param {Roo.form.Field} this
36957              */
36958             valid : true,
36959              /**
36960              * @event keyup
36961              * Fires after the key up
36962              * @param {Roo.form.Field} this
36963              * @param {Roo.EventObject}  e The event Object
36964              */
36965             keyup : true
36966         });
36967     },
36968
36969     /**
36970      * Returns the name attribute of the field if available
36971      * @return {String} name The field name
36972      */
36973     getName: function(){
36974          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36975     },
36976
36977     // private
36978     onRender : function(ct, position){
36979         Roo.form.Field.superclass.onRender.call(this, ct, position);
36980         if(!this.el){
36981             var cfg = this.getAutoCreate();
36982             if(!cfg.name){
36983                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36984             }
36985             if (!cfg.name.length) {
36986                 delete cfg.name;
36987             }
36988             if(this.inputType){
36989                 cfg.type = this.inputType;
36990             }
36991             this.el = ct.createChild(cfg, position);
36992         }
36993         var type = this.el.dom.type;
36994         if(type){
36995             if(type == 'password'){
36996                 type = 'text';
36997             }
36998             this.el.addClass('x-form-'+type);
36999         }
37000         if(this.readOnly){
37001             this.el.dom.readOnly = true;
37002         }
37003         if(this.tabIndex !== undefined){
37004             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37005         }
37006
37007         this.el.addClass([this.fieldClass, this.cls]);
37008         this.initValue();
37009     },
37010
37011     /**
37012      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37013      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37014      * @return {Roo.form.Field} this
37015      */
37016     applyTo : function(target){
37017         this.allowDomMove = false;
37018         this.el = Roo.get(target);
37019         this.render(this.el.dom.parentNode);
37020         return this;
37021     },
37022
37023     // private
37024     initValue : function(){
37025         if(this.value !== undefined){
37026             this.setValue(this.value);
37027         }else if(this.el.dom.value.length > 0){
37028             this.setValue(this.el.dom.value);
37029         }
37030     },
37031
37032     /**
37033      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37034      */
37035     isDirty : function() {
37036         if(this.disabled) {
37037             return false;
37038         }
37039         return String(this.getValue()) !== String(this.originalValue);
37040     },
37041
37042     // private
37043     afterRender : function(){
37044         Roo.form.Field.superclass.afterRender.call(this);
37045         this.initEvents();
37046     },
37047
37048     // private
37049     fireKey : function(e){
37050         //Roo.log('field ' + e.getKey());
37051         if(e.isNavKeyPress()){
37052             this.fireEvent("specialkey", this, e);
37053         }
37054     },
37055
37056     /**
37057      * Resets the current field value to the originally loaded value and clears any validation messages
37058      */
37059     reset : function(){
37060         this.setValue(this.resetValue);
37061         this.clearInvalid();
37062     },
37063
37064     // private
37065     initEvents : function(){
37066         // safari killled keypress - so keydown is now used..
37067         this.el.on("keydown" , this.fireKey,  this);
37068         this.el.on("focus", this.onFocus,  this);
37069         this.el.on("blur", this.onBlur,  this);
37070         this.el.relayEvent('keyup', this);
37071
37072         // reference to original value for reset
37073         this.originalValue = this.getValue();
37074         this.resetValue =  this.getValue();
37075     },
37076
37077     // private
37078     onFocus : function(){
37079         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37080             this.el.addClass(this.focusClass);
37081         }
37082         if(!this.hasFocus){
37083             this.hasFocus = true;
37084             this.startValue = this.getValue();
37085             this.fireEvent("focus", this);
37086         }
37087     },
37088
37089     beforeBlur : Roo.emptyFn,
37090
37091     // private
37092     onBlur : function(){
37093         this.beforeBlur();
37094         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37095             this.el.removeClass(this.focusClass);
37096         }
37097         this.hasFocus = false;
37098         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37099             this.validate();
37100         }
37101         var v = this.getValue();
37102         if(String(v) !== String(this.startValue)){
37103             this.fireEvent('change', this, v, this.startValue);
37104         }
37105         this.fireEvent("blur", this);
37106     },
37107
37108     /**
37109      * Returns whether or not the field value is currently valid
37110      * @param {Boolean} preventMark True to disable marking the field invalid
37111      * @return {Boolean} True if the value is valid, else false
37112      */
37113     isValid : function(preventMark){
37114         if(this.disabled){
37115             return true;
37116         }
37117         var restore = this.preventMark;
37118         this.preventMark = preventMark === true;
37119         var v = this.validateValue(this.processValue(this.getRawValue()));
37120         this.preventMark = restore;
37121         return v;
37122     },
37123
37124     /**
37125      * Validates the field value
37126      * @return {Boolean} True if the value is valid, else false
37127      */
37128     validate : function(){
37129         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37130             this.clearInvalid();
37131             return true;
37132         }
37133         return false;
37134     },
37135
37136     processValue : function(value){
37137         return value;
37138     },
37139
37140     // private
37141     // Subclasses should provide the validation implementation by overriding this
37142     validateValue : function(value){
37143         return true;
37144     },
37145
37146     /**
37147      * Mark this field as invalid
37148      * @param {String} msg The validation message
37149      */
37150     markInvalid : function(msg){
37151         if(!this.rendered || this.preventMark){ // not rendered
37152             return;
37153         }
37154         
37155         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37156         
37157         obj.el.addClass(this.invalidClass);
37158         msg = msg || this.invalidText;
37159         switch(this.msgTarget){
37160             case 'qtip':
37161                 obj.el.dom.qtip = msg;
37162                 obj.el.dom.qclass = 'x-form-invalid-tip';
37163                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37164                     Roo.QuickTips.enable();
37165                 }
37166                 break;
37167             case 'title':
37168                 this.el.dom.title = msg;
37169                 break;
37170             case 'under':
37171                 if(!this.errorEl){
37172                     var elp = this.el.findParent('.x-form-element', 5, true);
37173                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37174                     this.errorEl.setWidth(elp.getWidth(true)-20);
37175                 }
37176                 this.errorEl.update(msg);
37177                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37178                 break;
37179             case 'side':
37180                 if(!this.errorIcon){
37181                     var elp = this.el.findParent('.x-form-element', 5, true);
37182                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37183                 }
37184                 this.alignErrorIcon();
37185                 this.errorIcon.dom.qtip = msg;
37186                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37187                 this.errorIcon.show();
37188                 this.on('resize', this.alignErrorIcon, this);
37189                 break;
37190             default:
37191                 var t = Roo.getDom(this.msgTarget);
37192                 t.innerHTML = msg;
37193                 t.style.display = this.msgDisplay;
37194                 break;
37195         }
37196         this.fireEvent('invalid', this, msg);
37197     },
37198
37199     // private
37200     alignErrorIcon : function(){
37201         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37202     },
37203
37204     /**
37205      * Clear any invalid styles/messages for this field
37206      */
37207     clearInvalid : function(){
37208         if(!this.rendered || this.preventMark){ // not rendered
37209             return;
37210         }
37211         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37212         
37213         obj.el.removeClass(this.invalidClass);
37214         switch(this.msgTarget){
37215             case 'qtip':
37216                 obj.el.dom.qtip = '';
37217                 break;
37218             case 'title':
37219                 this.el.dom.title = '';
37220                 break;
37221             case 'under':
37222                 if(this.errorEl){
37223                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37224                 }
37225                 break;
37226             case 'side':
37227                 if(this.errorIcon){
37228                     this.errorIcon.dom.qtip = '';
37229                     this.errorIcon.hide();
37230                     this.un('resize', this.alignErrorIcon, this);
37231                 }
37232                 break;
37233             default:
37234                 var t = Roo.getDom(this.msgTarget);
37235                 t.innerHTML = '';
37236                 t.style.display = 'none';
37237                 break;
37238         }
37239         this.fireEvent('valid', this);
37240     },
37241
37242     /**
37243      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37244      * @return {Mixed} value The field value
37245      */
37246     getRawValue : function(){
37247         var v = this.el.getValue();
37248         
37249         return v;
37250     },
37251
37252     /**
37253      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37254      * @return {Mixed} value The field value
37255      */
37256     getValue : function(){
37257         var v = this.el.getValue();
37258          
37259         return v;
37260     },
37261
37262     /**
37263      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37264      * @param {Mixed} value The value to set
37265      */
37266     setRawValue : function(v){
37267         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37268     },
37269
37270     /**
37271      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37272      * @param {Mixed} value The value to set
37273      */
37274     setValue : function(v){
37275         this.value = v;
37276         if(this.rendered){
37277             this.el.dom.value = (v === null || v === undefined ? '' : v);
37278              this.validate();
37279         }
37280     },
37281
37282     adjustSize : function(w, h){
37283         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37284         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37285         return s;
37286     },
37287
37288     adjustWidth : function(tag, w){
37289         tag = tag.toLowerCase();
37290         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37291             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37292                 if(tag == 'input'){
37293                     return w + 2;
37294                 }
37295                 if(tag == 'textarea'){
37296                     return w-2;
37297                 }
37298             }else if(Roo.isOpera){
37299                 if(tag == 'input'){
37300                     return w + 2;
37301                 }
37302                 if(tag == 'textarea'){
37303                     return w-2;
37304                 }
37305             }
37306         }
37307         return w;
37308     }
37309 });
37310
37311
37312 // anything other than normal should be considered experimental
37313 Roo.form.Field.msgFx = {
37314     normal : {
37315         show: function(msgEl, f){
37316             msgEl.setDisplayed('block');
37317         },
37318
37319         hide : function(msgEl, f){
37320             msgEl.setDisplayed(false).update('');
37321         }
37322     },
37323
37324     slide : {
37325         show: function(msgEl, f){
37326             msgEl.slideIn('t', {stopFx:true});
37327         },
37328
37329         hide : function(msgEl, f){
37330             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37331         }
37332     },
37333
37334     slideRight : {
37335         show: function(msgEl, f){
37336             msgEl.fixDisplay();
37337             msgEl.alignTo(f.el, 'tl-tr');
37338             msgEl.slideIn('l', {stopFx:true});
37339         },
37340
37341         hide : function(msgEl, f){
37342             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37343         }
37344     }
37345 };/*
37346  * Based on:
37347  * Ext JS Library 1.1.1
37348  * Copyright(c) 2006-2007, Ext JS, LLC.
37349  *
37350  * Originally Released Under LGPL - original licence link has changed is not relivant.
37351  *
37352  * Fork - LGPL
37353  * <script type="text/javascript">
37354  */
37355  
37356
37357 /**
37358  * @class Roo.form.TextField
37359  * @extends Roo.form.Field
37360  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37361  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37362  * @constructor
37363  * Creates a new TextField
37364  * @param {Object} config Configuration options
37365  */
37366 Roo.form.TextField = function(config){
37367     Roo.form.TextField.superclass.constructor.call(this, config);
37368     this.addEvents({
37369         /**
37370          * @event autosize
37371          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37372          * according to the default logic, but this event provides a hook for the developer to apply additional
37373          * logic at runtime to resize the field if needed.
37374              * @param {Roo.form.Field} this This text field
37375              * @param {Number} width The new field width
37376              */
37377         autosize : true
37378     });
37379 };
37380
37381 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37382     /**
37383      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37384      */
37385     grow : false,
37386     /**
37387      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37388      */
37389     growMin : 30,
37390     /**
37391      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37392      */
37393     growMax : 800,
37394     /**
37395      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37396      */
37397     vtype : null,
37398     /**
37399      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37400      */
37401     maskRe : null,
37402     /**
37403      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37404      */
37405     disableKeyFilter : false,
37406     /**
37407      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37408      */
37409     allowBlank : true,
37410     /**
37411      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37412      */
37413     minLength : 0,
37414     /**
37415      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37416      */
37417     maxLength : Number.MAX_VALUE,
37418     /**
37419      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37420      */
37421     minLengthText : "The minimum length for this field is {0}",
37422     /**
37423      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37424      */
37425     maxLengthText : "The maximum length for this field is {0}",
37426     /**
37427      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37428      */
37429     selectOnFocus : false,
37430     /**
37431      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37432      */
37433     blankText : "This field is required",
37434     /**
37435      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37436      * If available, this function will be called only after the basic validators all return true, and will be passed the
37437      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37438      */
37439     validator : null,
37440     /**
37441      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37442      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37443      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37444      */
37445     regex : null,
37446     /**
37447      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37448      */
37449     regexText : "",
37450     /**
37451      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37452      */
37453     emptyText : null,
37454    
37455
37456     // private
37457     initEvents : function()
37458     {
37459         if (this.emptyText) {
37460             this.el.attr('placeholder', this.emptyText);
37461         }
37462         
37463         Roo.form.TextField.superclass.initEvents.call(this);
37464         if(this.validationEvent == 'keyup'){
37465             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37466             this.el.on('keyup', this.filterValidation, this);
37467         }
37468         else if(this.validationEvent !== false){
37469             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37470         }
37471         
37472         if(this.selectOnFocus){
37473             this.on("focus", this.preFocus, this);
37474             
37475         }
37476         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37477             this.el.on("keypress", this.filterKeys, this);
37478         }
37479         if(this.grow){
37480             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37481             this.el.on("click", this.autoSize,  this);
37482         }
37483         if(this.el.is('input[type=password]') && Roo.isSafari){
37484             this.el.on('keydown', this.SafariOnKeyDown, this);
37485         }
37486     },
37487
37488     processValue : function(value){
37489         if(this.stripCharsRe){
37490             var newValue = value.replace(this.stripCharsRe, '');
37491             if(newValue !== value){
37492                 this.setRawValue(newValue);
37493                 return newValue;
37494             }
37495         }
37496         return value;
37497     },
37498
37499     filterValidation : function(e){
37500         if(!e.isNavKeyPress()){
37501             this.validationTask.delay(this.validationDelay);
37502         }
37503     },
37504
37505     // private
37506     onKeyUp : function(e){
37507         if(!e.isNavKeyPress()){
37508             this.autoSize();
37509         }
37510     },
37511
37512     /**
37513      * Resets the current field value to the originally-loaded value and clears any validation messages.
37514      *  
37515      */
37516     reset : function(){
37517         Roo.form.TextField.superclass.reset.call(this);
37518        
37519     },
37520
37521     
37522     // private
37523     preFocus : function(){
37524         
37525         if(this.selectOnFocus){
37526             this.el.dom.select();
37527         }
37528     },
37529
37530     
37531     // private
37532     filterKeys : function(e){
37533         var k = e.getKey();
37534         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37535             return;
37536         }
37537         var c = e.getCharCode(), cc = String.fromCharCode(c);
37538         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37539             return;
37540         }
37541         if(!this.maskRe.test(cc)){
37542             e.stopEvent();
37543         }
37544     },
37545
37546     setValue : function(v){
37547         
37548         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37549         
37550         this.autoSize();
37551     },
37552
37553     /**
37554      * Validates a value according to the field's validation rules and marks the field as invalid
37555      * if the validation fails
37556      * @param {Mixed} value The value to validate
37557      * @return {Boolean} True if the value is valid, else false
37558      */
37559     validateValue : function(value){
37560         if(value.length < 1)  { // if it's blank
37561              if(this.allowBlank){
37562                 this.clearInvalid();
37563                 return true;
37564              }else{
37565                 this.markInvalid(this.blankText);
37566                 return false;
37567              }
37568         }
37569         if(value.length < this.minLength){
37570             this.markInvalid(String.format(this.minLengthText, this.minLength));
37571             return false;
37572         }
37573         if(value.length > this.maxLength){
37574             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37575             return false;
37576         }
37577         if(this.vtype){
37578             var vt = Roo.form.VTypes;
37579             if(!vt[this.vtype](value, this)){
37580                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37581                 return false;
37582             }
37583         }
37584         if(typeof this.validator == "function"){
37585             var msg = this.validator(value);
37586             if(msg !== true){
37587                 this.markInvalid(msg);
37588                 return false;
37589             }
37590         }
37591         if(this.regex && !this.regex.test(value)){
37592             this.markInvalid(this.regexText);
37593             return false;
37594         }
37595         return true;
37596     },
37597
37598     /**
37599      * Selects text in this field
37600      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37601      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37602      */
37603     selectText : function(start, end){
37604         var v = this.getRawValue();
37605         if(v.length > 0){
37606             start = start === undefined ? 0 : start;
37607             end = end === undefined ? v.length : end;
37608             var d = this.el.dom;
37609             if(d.setSelectionRange){
37610                 d.setSelectionRange(start, end);
37611             }else if(d.createTextRange){
37612                 var range = d.createTextRange();
37613                 range.moveStart("character", start);
37614                 range.moveEnd("character", v.length-end);
37615                 range.select();
37616             }
37617         }
37618     },
37619
37620     /**
37621      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37622      * This only takes effect if grow = true, and fires the autosize event.
37623      */
37624     autoSize : function(){
37625         if(!this.grow || !this.rendered){
37626             return;
37627         }
37628         if(!this.metrics){
37629             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37630         }
37631         var el = this.el;
37632         var v = el.dom.value;
37633         var d = document.createElement('div');
37634         d.appendChild(document.createTextNode(v));
37635         v = d.innerHTML;
37636         d = null;
37637         v += "&#160;";
37638         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37639         this.el.setWidth(w);
37640         this.fireEvent("autosize", this, w);
37641     },
37642     
37643     // private
37644     SafariOnKeyDown : function(event)
37645     {
37646         // this is a workaround for a password hang bug on chrome/ webkit.
37647         
37648         var isSelectAll = false;
37649         
37650         if(this.el.dom.selectionEnd > 0){
37651             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37652         }
37653         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37654             event.preventDefault();
37655             this.setValue('');
37656             return;
37657         }
37658         
37659         if(isSelectAll){ // backspace and delete key
37660             
37661             event.preventDefault();
37662             // this is very hacky as keydown always get's upper case.
37663             //
37664             var cc = String.fromCharCode(event.getCharCode());
37665             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37666             
37667         }
37668         
37669         
37670     }
37671 });/*
37672  * Based on:
37673  * Ext JS Library 1.1.1
37674  * Copyright(c) 2006-2007, Ext JS, LLC.
37675  *
37676  * Originally Released Under LGPL - original licence link has changed is not relivant.
37677  *
37678  * Fork - LGPL
37679  * <script type="text/javascript">
37680  */
37681  
37682 /**
37683  * @class Roo.form.Hidden
37684  * @extends Roo.form.TextField
37685  * Simple Hidden element used on forms 
37686  * 
37687  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37688  * 
37689  * @constructor
37690  * Creates a new Hidden form element.
37691  * @param {Object} config Configuration options
37692  */
37693
37694
37695
37696 // easy hidden field...
37697 Roo.form.Hidden = function(config){
37698     Roo.form.Hidden.superclass.constructor.call(this, config);
37699 };
37700   
37701 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37702     fieldLabel:      '',
37703     inputType:      'hidden',
37704     width:          50,
37705     allowBlank:     true,
37706     labelSeparator: '',
37707     hidden:         true,
37708     itemCls :       'x-form-item-display-none'
37709
37710
37711 });
37712
37713
37714 /*
37715  * Based on:
37716  * Ext JS Library 1.1.1
37717  * Copyright(c) 2006-2007, Ext JS, LLC.
37718  *
37719  * Originally Released Under LGPL - original licence link has changed is not relivant.
37720  *
37721  * Fork - LGPL
37722  * <script type="text/javascript">
37723  */
37724  
37725 /**
37726  * @class Roo.form.TriggerField
37727  * @extends Roo.form.TextField
37728  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37729  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37730  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37731  * for which you can provide a custom implementation.  For example:
37732  * <pre><code>
37733 var trigger = new Roo.form.TriggerField();
37734 trigger.onTriggerClick = myTriggerFn;
37735 trigger.applyTo('my-field');
37736 </code></pre>
37737  *
37738  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37739  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37740  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37741  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37742  * @constructor
37743  * Create a new TriggerField.
37744  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37745  * to the base TextField)
37746  */
37747 Roo.form.TriggerField = function(config){
37748     this.mimicing = false;
37749     Roo.form.TriggerField.superclass.constructor.call(this, config);
37750 };
37751
37752 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37753     /**
37754      * @cfg {String} triggerClass A CSS class to apply to the trigger
37755      */
37756     /**
37757      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37758      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37759      */
37760     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37761     /**
37762      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37763      */
37764     hideTrigger:false,
37765
37766     /** @cfg {Boolean} grow @hide */
37767     /** @cfg {Number} growMin @hide */
37768     /** @cfg {Number} growMax @hide */
37769
37770     /**
37771      * @hide 
37772      * @method
37773      */
37774     autoSize: Roo.emptyFn,
37775     // private
37776     monitorTab : true,
37777     // private
37778     deferHeight : true,
37779
37780     
37781     actionMode : 'wrap',
37782     // private
37783     onResize : function(w, h){
37784         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37785         if(typeof w == 'number'){
37786             var x = w - this.trigger.getWidth();
37787             this.el.setWidth(this.adjustWidth('input', x));
37788             this.trigger.setStyle('left', x+'px');
37789         }
37790     },
37791
37792     // private
37793     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37794
37795     // private
37796     getResizeEl : function(){
37797         return this.wrap;
37798     },
37799
37800     // private
37801     getPositionEl : function(){
37802         return this.wrap;
37803     },
37804
37805     // private
37806     alignErrorIcon : function(){
37807         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37808     },
37809
37810     // private
37811     onRender : function(ct, position){
37812         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37813         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37814         this.trigger = this.wrap.createChild(this.triggerConfig ||
37815                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37816         if(this.hideTrigger){
37817             this.trigger.setDisplayed(false);
37818         }
37819         this.initTrigger();
37820         if(!this.width){
37821             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37822         }
37823     },
37824
37825     // private
37826     initTrigger : function(){
37827         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37828         this.trigger.addClassOnOver('x-form-trigger-over');
37829         this.trigger.addClassOnClick('x-form-trigger-click');
37830     },
37831
37832     // private
37833     onDestroy : function(){
37834         if(this.trigger){
37835             this.trigger.removeAllListeners();
37836             this.trigger.remove();
37837         }
37838         if(this.wrap){
37839             this.wrap.remove();
37840         }
37841         Roo.form.TriggerField.superclass.onDestroy.call(this);
37842     },
37843
37844     // private
37845     onFocus : function(){
37846         Roo.form.TriggerField.superclass.onFocus.call(this);
37847         if(!this.mimicing){
37848             this.wrap.addClass('x-trigger-wrap-focus');
37849             this.mimicing = true;
37850             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37851             if(this.monitorTab){
37852                 this.el.on("keydown", this.checkTab, this);
37853             }
37854         }
37855     },
37856
37857     // private
37858     checkTab : function(e){
37859         if(e.getKey() == e.TAB){
37860             this.triggerBlur();
37861         }
37862     },
37863
37864     // private
37865     onBlur : function(){
37866         // do nothing
37867     },
37868
37869     // private
37870     mimicBlur : function(e, t){
37871         if(!this.wrap.contains(t) && this.validateBlur()){
37872             this.triggerBlur();
37873         }
37874     },
37875
37876     // private
37877     triggerBlur : function(){
37878         this.mimicing = false;
37879         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37880         if(this.monitorTab){
37881             this.el.un("keydown", this.checkTab, this);
37882         }
37883         this.wrap.removeClass('x-trigger-wrap-focus');
37884         Roo.form.TriggerField.superclass.onBlur.call(this);
37885     },
37886
37887     // private
37888     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37889     validateBlur : function(e, t){
37890         return true;
37891     },
37892
37893     // private
37894     onDisable : function(){
37895         Roo.form.TriggerField.superclass.onDisable.call(this);
37896         if(this.wrap){
37897             this.wrap.addClass('x-item-disabled');
37898         }
37899     },
37900
37901     // private
37902     onEnable : function(){
37903         Roo.form.TriggerField.superclass.onEnable.call(this);
37904         if(this.wrap){
37905             this.wrap.removeClass('x-item-disabled');
37906         }
37907     },
37908
37909     // private
37910     onShow : function(){
37911         var ae = this.getActionEl();
37912         
37913         if(ae){
37914             ae.dom.style.display = '';
37915             ae.dom.style.visibility = 'visible';
37916         }
37917     },
37918
37919     // private
37920     
37921     onHide : function(){
37922         var ae = this.getActionEl();
37923         ae.dom.style.display = 'none';
37924     },
37925
37926     /**
37927      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37928      * by an implementing function.
37929      * @method
37930      * @param {EventObject} e
37931      */
37932     onTriggerClick : Roo.emptyFn
37933 });
37934
37935 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37936 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37937 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37938 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37939     initComponent : function(){
37940         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37941
37942         this.triggerConfig = {
37943             tag:'span', cls:'x-form-twin-triggers', cn:[
37944             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37945             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37946         ]};
37947     },
37948
37949     getTrigger : function(index){
37950         return this.triggers[index];
37951     },
37952
37953     initTrigger : function(){
37954         var ts = this.trigger.select('.x-form-trigger', true);
37955         this.wrap.setStyle('overflow', 'hidden');
37956         var triggerField = this;
37957         ts.each(function(t, all, index){
37958             t.hide = function(){
37959                 var w = triggerField.wrap.getWidth();
37960                 this.dom.style.display = 'none';
37961                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37962             };
37963             t.show = function(){
37964                 var w = triggerField.wrap.getWidth();
37965                 this.dom.style.display = '';
37966                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37967             };
37968             var triggerIndex = 'Trigger'+(index+1);
37969
37970             if(this['hide'+triggerIndex]){
37971                 t.dom.style.display = 'none';
37972             }
37973             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37974             t.addClassOnOver('x-form-trigger-over');
37975             t.addClassOnClick('x-form-trigger-click');
37976         }, this);
37977         this.triggers = ts.elements;
37978     },
37979
37980     onTrigger1Click : Roo.emptyFn,
37981     onTrigger2Click : Roo.emptyFn
37982 });/*
37983  * Based on:
37984  * Ext JS Library 1.1.1
37985  * Copyright(c) 2006-2007, Ext JS, LLC.
37986  *
37987  * Originally Released Under LGPL - original licence link has changed is not relivant.
37988  *
37989  * Fork - LGPL
37990  * <script type="text/javascript">
37991  */
37992  
37993 /**
37994  * @class Roo.form.TextArea
37995  * @extends Roo.form.TextField
37996  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37997  * support for auto-sizing.
37998  * @constructor
37999  * Creates a new TextArea
38000  * @param {Object} config Configuration options
38001  */
38002 Roo.form.TextArea = function(config){
38003     Roo.form.TextArea.superclass.constructor.call(this, config);
38004     // these are provided exchanges for backwards compat
38005     // minHeight/maxHeight were replaced by growMin/growMax to be
38006     // compatible with TextField growing config values
38007     if(this.minHeight !== undefined){
38008         this.growMin = this.minHeight;
38009     }
38010     if(this.maxHeight !== undefined){
38011         this.growMax = this.maxHeight;
38012     }
38013 };
38014
38015 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38016     /**
38017      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38018      */
38019     growMin : 60,
38020     /**
38021      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38022      */
38023     growMax: 1000,
38024     /**
38025      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38026      * in the field (equivalent to setting overflow: hidden, defaults to false)
38027      */
38028     preventScrollbars: false,
38029     /**
38030      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38031      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38032      */
38033
38034     // private
38035     onRender : function(ct, position){
38036         if(!this.el){
38037             this.defaultAutoCreate = {
38038                 tag: "textarea",
38039                 style:"width:300px;height:60px;",
38040                 autocomplete: "off"
38041             };
38042         }
38043         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38044         if(this.grow){
38045             this.textSizeEl = Roo.DomHelper.append(document.body, {
38046                 tag: "pre", cls: "x-form-grow-sizer"
38047             });
38048             if(this.preventScrollbars){
38049                 this.el.setStyle("overflow", "hidden");
38050             }
38051             this.el.setHeight(this.growMin);
38052         }
38053     },
38054
38055     onDestroy : function(){
38056         if(this.textSizeEl){
38057             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38058         }
38059         Roo.form.TextArea.superclass.onDestroy.call(this);
38060     },
38061
38062     // private
38063     onKeyUp : function(e){
38064         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38065             this.autoSize();
38066         }
38067     },
38068
38069     /**
38070      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38071      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38072      */
38073     autoSize : function(){
38074         if(!this.grow || !this.textSizeEl){
38075             return;
38076         }
38077         var el = this.el;
38078         var v = el.dom.value;
38079         var ts = this.textSizeEl;
38080
38081         ts.innerHTML = '';
38082         ts.appendChild(document.createTextNode(v));
38083         v = ts.innerHTML;
38084
38085         Roo.fly(ts).setWidth(this.el.getWidth());
38086         if(v.length < 1){
38087             v = "&#160;&#160;";
38088         }else{
38089             if(Roo.isIE){
38090                 v = v.replace(/\n/g, '<p>&#160;</p>');
38091             }
38092             v += "&#160;\n&#160;";
38093         }
38094         ts.innerHTML = v;
38095         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38096         if(h != this.lastHeight){
38097             this.lastHeight = h;
38098             this.el.setHeight(h);
38099             this.fireEvent("autosize", this, h);
38100         }
38101     }
38102 });/*
38103  * Based on:
38104  * Ext JS Library 1.1.1
38105  * Copyright(c) 2006-2007, Ext JS, LLC.
38106  *
38107  * Originally Released Under LGPL - original licence link has changed is not relivant.
38108  *
38109  * Fork - LGPL
38110  * <script type="text/javascript">
38111  */
38112  
38113
38114 /**
38115  * @class Roo.form.NumberField
38116  * @extends Roo.form.TextField
38117  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38118  * @constructor
38119  * Creates a new NumberField
38120  * @param {Object} config Configuration options
38121  */
38122 Roo.form.NumberField = function(config){
38123     Roo.form.NumberField.superclass.constructor.call(this, config);
38124 };
38125
38126 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38127     /**
38128      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38129      */
38130     fieldClass: "x-form-field x-form-num-field",
38131     /**
38132      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38133      */
38134     allowDecimals : true,
38135     /**
38136      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38137      */
38138     decimalSeparator : ".",
38139     /**
38140      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38141      */
38142     decimalPrecision : 2,
38143     /**
38144      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38145      */
38146     allowNegative : true,
38147     /**
38148      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38149      */
38150     minValue : Number.NEGATIVE_INFINITY,
38151     /**
38152      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38153      */
38154     maxValue : Number.MAX_VALUE,
38155     /**
38156      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38157      */
38158     minText : "The minimum value for this field is {0}",
38159     /**
38160      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38161      */
38162     maxText : "The maximum value for this field is {0}",
38163     /**
38164      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38165      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38166      */
38167     nanText : "{0} is not a valid number",
38168
38169     // private
38170     initEvents : function(){
38171         Roo.form.NumberField.superclass.initEvents.call(this);
38172         var allowed = "0123456789";
38173         if(this.allowDecimals){
38174             allowed += this.decimalSeparator;
38175         }
38176         if(this.allowNegative){
38177             allowed += "-";
38178         }
38179         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38180         var keyPress = function(e){
38181             var k = e.getKey();
38182             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38183                 return;
38184             }
38185             var c = e.getCharCode();
38186             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38187                 e.stopEvent();
38188             }
38189         };
38190         this.el.on("keypress", keyPress, this);
38191     },
38192
38193     // private
38194     validateValue : function(value){
38195         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38196             return false;
38197         }
38198         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38199              return true;
38200         }
38201         var num = this.parseValue(value);
38202         if(isNaN(num)){
38203             this.markInvalid(String.format(this.nanText, value));
38204             return false;
38205         }
38206         if(num < this.minValue){
38207             this.markInvalid(String.format(this.minText, this.minValue));
38208             return false;
38209         }
38210         if(num > this.maxValue){
38211             this.markInvalid(String.format(this.maxText, this.maxValue));
38212             return false;
38213         }
38214         return true;
38215     },
38216
38217     getValue : function(){
38218         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38219     },
38220
38221     // private
38222     parseValue : function(value){
38223         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38224         return isNaN(value) ? '' : value;
38225     },
38226
38227     // private
38228     fixPrecision : function(value){
38229         var nan = isNaN(value);
38230         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38231             return nan ? '' : value;
38232         }
38233         return parseFloat(value).toFixed(this.decimalPrecision);
38234     },
38235
38236     setValue : function(v){
38237         v = this.fixPrecision(v);
38238         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38239     },
38240
38241     // private
38242     decimalPrecisionFcn : function(v){
38243         return Math.floor(v);
38244     },
38245
38246     beforeBlur : function(){
38247         var v = this.parseValue(this.getRawValue());
38248         if(v){
38249             this.setValue(v);
38250         }
38251     }
38252 });/*
38253  * Based on:
38254  * Ext JS Library 1.1.1
38255  * Copyright(c) 2006-2007, Ext JS, LLC.
38256  *
38257  * Originally Released Under LGPL - original licence link has changed is not relivant.
38258  *
38259  * Fork - LGPL
38260  * <script type="text/javascript">
38261  */
38262  
38263 /**
38264  * @class Roo.form.DateField
38265  * @extends Roo.form.TriggerField
38266  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38267 * @constructor
38268 * Create a new DateField
38269 * @param {Object} config
38270  */
38271 Roo.form.DateField = function(config){
38272     Roo.form.DateField.superclass.constructor.call(this, config);
38273     
38274       this.addEvents({
38275          
38276         /**
38277          * @event select
38278          * Fires when a date is selected
38279              * @param {Roo.form.DateField} combo This combo box
38280              * @param {Date} date The date selected
38281              */
38282         'select' : true
38283          
38284     });
38285     
38286     
38287     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38288     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38289     this.ddMatch = null;
38290     if(this.disabledDates){
38291         var dd = this.disabledDates;
38292         var re = "(?:";
38293         for(var i = 0; i < dd.length; i++){
38294             re += dd[i];
38295             if(i != dd.length-1) re += "|";
38296         }
38297         this.ddMatch = new RegExp(re + ")");
38298     }
38299 };
38300
38301 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38302     /**
38303      * @cfg {String} format
38304      * The default date format string which can be overriden for localization support.  The format must be
38305      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38306      */
38307     format : "m/d/y",
38308     /**
38309      * @cfg {String} altFormats
38310      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38311      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38312      */
38313     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38314     /**
38315      * @cfg {Array} disabledDays
38316      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38317      */
38318     disabledDays : null,
38319     /**
38320      * @cfg {String} disabledDaysText
38321      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38322      */
38323     disabledDaysText : "Disabled",
38324     /**
38325      * @cfg {Array} disabledDates
38326      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38327      * expression so they are very powerful. Some examples:
38328      * <ul>
38329      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38330      * <li>["03/08", "09/16"] would disable those days for every year</li>
38331      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38332      * <li>["03/../2006"] would disable every day in March 2006</li>
38333      * <li>["^03"] would disable every day in every March</li>
38334      * </ul>
38335      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38336      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38337      */
38338     disabledDates : null,
38339     /**
38340      * @cfg {String} disabledDatesText
38341      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38342      */
38343     disabledDatesText : "Disabled",
38344     /**
38345      * @cfg {Date/String} minValue
38346      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38347      * valid format (defaults to null).
38348      */
38349     minValue : null,
38350     /**
38351      * @cfg {Date/String} maxValue
38352      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38353      * valid format (defaults to null).
38354      */
38355     maxValue : null,
38356     /**
38357      * @cfg {String} minText
38358      * The error text to display when the date in the cell is before minValue (defaults to
38359      * 'The date in this field must be after {minValue}').
38360      */
38361     minText : "The date in this field must be equal to or after {0}",
38362     /**
38363      * @cfg {String} maxText
38364      * The error text to display when the date in the cell is after maxValue (defaults to
38365      * 'The date in this field must be before {maxValue}').
38366      */
38367     maxText : "The date in this field must be equal to or before {0}",
38368     /**
38369      * @cfg {String} invalidText
38370      * The error text to display when the date in the field is invalid (defaults to
38371      * '{value} is not a valid date - it must be in the format {format}').
38372      */
38373     invalidText : "{0} is not a valid date - it must be in the format {1}",
38374     /**
38375      * @cfg {String} triggerClass
38376      * An additional CSS class used to style the trigger button.  The trigger will always get the
38377      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38378      * which displays a calendar icon).
38379      */
38380     triggerClass : 'x-form-date-trigger',
38381     
38382
38383     /**
38384      * @cfg {Boolean} useIso
38385      * if enabled, then the date field will use a hidden field to store the 
38386      * real value as iso formated date. default (false)
38387      */ 
38388     useIso : false,
38389     /**
38390      * @cfg {String/Object} autoCreate
38391      * A DomHelper element spec, or true for a default element spec (defaults to
38392      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38393      */ 
38394     // private
38395     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38396     
38397     // private
38398     hiddenField: false,
38399     
38400     onRender : function(ct, position)
38401     {
38402         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38403         if (this.useIso) {
38404             //this.el.dom.removeAttribute('name'); 
38405             Roo.log("Changing name?");
38406             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38407             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38408                     'before', true);
38409             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38410             // prevent input submission
38411             this.hiddenName = this.name;
38412         }
38413             
38414             
38415     },
38416     
38417     // private
38418     validateValue : function(value)
38419     {
38420         value = this.formatDate(value);
38421         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38422             Roo.log('super failed');
38423             return false;
38424         }
38425         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38426              return true;
38427         }
38428         var svalue = value;
38429         value = this.parseDate(value);
38430         if(!value){
38431             Roo.log('parse date failed' + svalue);
38432             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38433             return false;
38434         }
38435         var time = value.getTime();
38436         if(this.minValue && time < this.minValue.getTime()){
38437             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38438             return false;
38439         }
38440         if(this.maxValue && time > this.maxValue.getTime()){
38441             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38442             return false;
38443         }
38444         if(this.disabledDays){
38445             var day = value.getDay();
38446             for(var i = 0; i < this.disabledDays.length; i++) {
38447                 if(day === this.disabledDays[i]){
38448                     this.markInvalid(this.disabledDaysText);
38449                     return false;
38450                 }
38451             }
38452         }
38453         var fvalue = this.formatDate(value);
38454         if(this.ddMatch && this.ddMatch.test(fvalue)){
38455             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38456             return false;
38457         }
38458         return true;
38459     },
38460
38461     // private
38462     // Provides logic to override the default TriggerField.validateBlur which just returns true
38463     validateBlur : function(){
38464         return !this.menu || !this.menu.isVisible();
38465     },
38466     
38467     getName: function()
38468     {
38469         // returns hidden if it's set..
38470         if (!this.rendered) {return ''};
38471         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38472         
38473     },
38474
38475     /**
38476      * Returns the current date value of the date field.
38477      * @return {Date} The date value
38478      */
38479     getValue : function(){
38480         
38481         return  this.hiddenField ?
38482                 this.hiddenField.value :
38483                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38484     },
38485
38486     /**
38487      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38488      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38489      * (the default format used is "m/d/y").
38490      * <br />Usage:
38491      * <pre><code>
38492 //All of these calls set the same date value (May 4, 2006)
38493
38494 //Pass a date object:
38495 var dt = new Date('5/4/06');
38496 dateField.setValue(dt);
38497
38498 //Pass a date string (default format):
38499 dateField.setValue('5/4/06');
38500
38501 //Pass a date string (custom format):
38502 dateField.format = 'Y-m-d';
38503 dateField.setValue('2006-5-4');
38504 </code></pre>
38505      * @param {String/Date} date The date or valid date string
38506      */
38507     setValue : function(date){
38508         if (this.hiddenField) {
38509             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38510         }
38511         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38512         // make sure the value field is always stored as a date..
38513         this.value = this.parseDate(date);
38514         
38515         
38516     },
38517
38518     // private
38519     parseDate : function(value){
38520         if(!value || value instanceof Date){
38521             return value;
38522         }
38523         var v = Date.parseDate(value, this.format);
38524          if (!v && this.useIso) {
38525             v = Date.parseDate(value, 'Y-m-d');
38526         }
38527         if(!v && this.altFormats){
38528             if(!this.altFormatsArray){
38529                 this.altFormatsArray = this.altFormats.split("|");
38530             }
38531             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38532                 v = Date.parseDate(value, this.altFormatsArray[i]);
38533             }
38534         }
38535         return v;
38536     },
38537
38538     // private
38539     formatDate : function(date, fmt){
38540         return (!date || !(date instanceof Date)) ?
38541                date : date.dateFormat(fmt || this.format);
38542     },
38543
38544     // private
38545     menuListeners : {
38546         select: function(m, d){
38547             
38548             this.setValue(d);
38549             this.fireEvent('select', this, d);
38550         },
38551         show : function(){ // retain focus styling
38552             this.onFocus();
38553         },
38554         hide : function(){
38555             this.focus.defer(10, this);
38556             var ml = this.menuListeners;
38557             this.menu.un("select", ml.select,  this);
38558             this.menu.un("show", ml.show,  this);
38559             this.menu.un("hide", ml.hide,  this);
38560         }
38561     },
38562
38563     // private
38564     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38565     onTriggerClick : function(){
38566         if(this.disabled){
38567             return;
38568         }
38569         if(this.menu == null){
38570             this.menu = new Roo.menu.DateMenu();
38571         }
38572         Roo.apply(this.menu.picker,  {
38573             showClear: this.allowBlank,
38574             minDate : this.minValue,
38575             maxDate : this.maxValue,
38576             disabledDatesRE : this.ddMatch,
38577             disabledDatesText : this.disabledDatesText,
38578             disabledDays : this.disabledDays,
38579             disabledDaysText : this.disabledDaysText,
38580             format : this.useIso ? 'Y-m-d' : this.format,
38581             minText : String.format(this.minText, this.formatDate(this.minValue)),
38582             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38583         });
38584         this.menu.on(Roo.apply({}, this.menuListeners, {
38585             scope:this
38586         }));
38587         this.menu.picker.setValue(this.getValue() || new Date());
38588         this.menu.show(this.el, "tl-bl?");
38589     },
38590
38591     beforeBlur : function(){
38592         var v = this.parseDate(this.getRawValue());
38593         if(v){
38594             this.setValue(v);
38595         }
38596     },
38597
38598     /*@
38599      * overide
38600      * 
38601      */
38602     isDirty : function() {
38603         if(this.disabled) {
38604             return false;
38605         }
38606         
38607         if(typeof(this.startValue) === 'undefined'){
38608             return false;
38609         }
38610         
38611         return String(this.getValue()) !== String(this.startValue);
38612         
38613     }
38614 });/*
38615  * Based on:
38616  * Ext JS Library 1.1.1
38617  * Copyright(c) 2006-2007, Ext JS, LLC.
38618  *
38619  * Originally Released Under LGPL - original licence link has changed is not relivant.
38620  *
38621  * Fork - LGPL
38622  * <script type="text/javascript">
38623  */
38624  
38625 /**
38626  * @class Roo.form.MonthField
38627  * @extends Roo.form.TriggerField
38628  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38629 * @constructor
38630 * Create a new MonthField
38631 * @param {Object} config
38632  */
38633 Roo.form.MonthField = function(config){
38634     
38635     Roo.form.MonthField.superclass.constructor.call(this, config);
38636     
38637       this.addEvents({
38638          
38639         /**
38640          * @event select
38641          * Fires when a date is selected
38642              * @param {Roo.form.MonthFieeld} combo This combo box
38643              * @param {Date} date The date selected
38644              */
38645         'select' : true
38646          
38647     });
38648     
38649     
38650     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38651     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38652     this.ddMatch = null;
38653     if(this.disabledDates){
38654         var dd = this.disabledDates;
38655         var re = "(?:";
38656         for(var i = 0; i < dd.length; i++){
38657             re += dd[i];
38658             if(i != dd.length-1) re += "|";
38659         }
38660         this.ddMatch = new RegExp(re + ")");
38661     }
38662 };
38663
38664 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38665     /**
38666      * @cfg {String} format
38667      * The default date format string which can be overriden for localization support.  The format must be
38668      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38669      */
38670     format : "M Y",
38671     /**
38672      * @cfg {String} altFormats
38673      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38674      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38675      */
38676     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38677     /**
38678      * @cfg {Array} disabledDays
38679      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38680      */
38681     disabledDays : [0,1,2,3,4,5,6],
38682     /**
38683      * @cfg {String} disabledDaysText
38684      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38685      */
38686     disabledDaysText : "Disabled",
38687     /**
38688      * @cfg {Array} disabledDates
38689      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38690      * expression so they are very powerful. Some examples:
38691      * <ul>
38692      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38693      * <li>["03/08", "09/16"] would disable those days for every year</li>
38694      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38695      * <li>["03/../2006"] would disable every day in March 2006</li>
38696      * <li>["^03"] would disable every day in every March</li>
38697      * </ul>
38698      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38699      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38700      */
38701     disabledDates : null,
38702     /**
38703      * @cfg {String} disabledDatesText
38704      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38705      */
38706     disabledDatesText : "Disabled",
38707     /**
38708      * @cfg {Date/String} minValue
38709      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38710      * valid format (defaults to null).
38711      */
38712     minValue : null,
38713     /**
38714      * @cfg {Date/String} maxValue
38715      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38716      * valid format (defaults to null).
38717      */
38718     maxValue : null,
38719     /**
38720      * @cfg {String} minText
38721      * The error text to display when the date in the cell is before minValue (defaults to
38722      * 'The date in this field must be after {minValue}').
38723      */
38724     minText : "The date in this field must be equal to or after {0}",
38725     /**
38726      * @cfg {String} maxTextf
38727      * The error text to display when the date in the cell is after maxValue (defaults to
38728      * 'The date in this field must be before {maxValue}').
38729      */
38730     maxText : "The date in this field must be equal to or before {0}",
38731     /**
38732      * @cfg {String} invalidText
38733      * The error text to display when the date in the field is invalid (defaults to
38734      * '{value} is not a valid date - it must be in the format {format}').
38735      */
38736     invalidText : "{0} is not a valid date - it must be in the format {1}",
38737     /**
38738      * @cfg {String} triggerClass
38739      * An additional CSS class used to style the trigger button.  The trigger will always get the
38740      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38741      * which displays a calendar icon).
38742      */
38743     triggerClass : 'x-form-date-trigger',
38744     
38745
38746     /**
38747      * @cfg {Boolean} useIso
38748      * if enabled, then the date field will use a hidden field to store the 
38749      * real value as iso formated date. default (true)
38750      */ 
38751     useIso : true,
38752     /**
38753      * @cfg {String/Object} autoCreate
38754      * A DomHelper element spec, or true for a default element spec (defaults to
38755      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38756      */ 
38757     // private
38758     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38759     
38760     // private
38761     hiddenField: false,
38762     
38763     hideMonthPicker : false,
38764     
38765     onRender : function(ct, position)
38766     {
38767         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38768         if (this.useIso) {
38769             this.el.dom.removeAttribute('name'); 
38770             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38771                     'before', true);
38772             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38773             // prevent input submission
38774             this.hiddenName = this.name;
38775         }
38776             
38777             
38778     },
38779     
38780     // private
38781     validateValue : function(value)
38782     {
38783         value = this.formatDate(value);
38784         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38785             return false;
38786         }
38787         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38788              return true;
38789         }
38790         var svalue = value;
38791         value = this.parseDate(value);
38792         if(!value){
38793             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38794             return false;
38795         }
38796         var time = value.getTime();
38797         if(this.minValue && time < this.minValue.getTime()){
38798             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38799             return false;
38800         }
38801         if(this.maxValue && time > this.maxValue.getTime()){
38802             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38803             return false;
38804         }
38805         /*if(this.disabledDays){
38806             var day = value.getDay();
38807             for(var i = 0; i < this.disabledDays.length; i++) {
38808                 if(day === this.disabledDays[i]){
38809                     this.markInvalid(this.disabledDaysText);
38810                     return false;
38811                 }
38812             }
38813         }
38814         */
38815         var fvalue = this.formatDate(value);
38816         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38817             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38818             return false;
38819         }
38820         */
38821         return true;
38822     },
38823
38824     // private
38825     // Provides logic to override the default TriggerField.validateBlur which just returns true
38826     validateBlur : function(){
38827         return !this.menu || !this.menu.isVisible();
38828     },
38829
38830     /**
38831      * Returns the current date value of the date field.
38832      * @return {Date} The date value
38833      */
38834     getValue : function(){
38835         
38836         
38837         
38838         return  this.hiddenField ?
38839                 this.hiddenField.value :
38840                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38841     },
38842
38843     /**
38844      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38845      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38846      * (the default format used is "m/d/y").
38847      * <br />Usage:
38848      * <pre><code>
38849 //All of these calls set the same date value (May 4, 2006)
38850
38851 //Pass a date object:
38852 var dt = new Date('5/4/06');
38853 monthField.setValue(dt);
38854
38855 //Pass a date string (default format):
38856 monthField.setValue('5/4/06');
38857
38858 //Pass a date string (custom format):
38859 monthField.format = 'Y-m-d';
38860 monthField.setValue('2006-5-4');
38861 </code></pre>
38862      * @param {String/Date} date The date or valid date string
38863      */
38864     setValue : function(date){
38865         Roo.log('month setValue' + date);
38866         // can only be first of month..
38867         
38868         var val = this.parseDate(date);
38869         
38870         if (this.hiddenField) {
38871             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38872         }
38873         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38874         this.value = this.parseDate(date);
38875     },
38876
38877     // private
38878     parseDate : function(value){
38879         if(!value || value instanceof Date){
38880             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38881             return value;
38882         }
38883         var v = Date.parseDate(value, this.format);
38884         if (!v && this.useIso) {
38885             v = Date.parseDate(value, 'Y-m-d');
38886         }
38887         if (v) {
38888             // 
38889             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38890         }
38891         
38892         
38893         if(!v && this.altFormats){
38894             if(!this.altFormatsArray){
38895                 this.altFormatsArray = this.altFormats.split("|");
38896             }
38897             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38898                 v = Date.parseDate(value, this.altFormatsArray[i]);
38899             }
38900         }
38901         return v;
38902     },
38903
38904     // private
38905     formatDate : function(date, fmt){
38906         return (!date || !(date instanceof Date)) ?
38907                date : date.dateFormat(fmt || this.format);
38908     },
38909
38910     // private
38911     menuListeners : {
38912         select: function(m, d){
38913             this.setValue(d);
38914             this.fireEvent('select', this, d);
38915         },
38916         show : function(){ // retain focus styling
38917             this.onFocus();
38918         },
38919         hide : function(){
38920             this.focus.defer(10, this);
38921             var ml = this.menuListeners;
38922             this.menu.un("select", ml.select,  this);
38923             this.menu.un("show", ml.show,  this);
38924             this.menu.un("hide", ml.hide,  this);
38925         }
38926     },
38927     // private
38928     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38929     onTriggerClick : function(){
38930         if(this.disabled){
38931             return;
38932         }
38933         if(this.menu == null){
38934             this.menu = new Roo.menu.DateMenu();
38935            
38936         }
38937         
38938         Roo.apply(this.menu.picker,  {
38939             
38940             showClear: this.allowBlank,
38941             minDate : this.minValue,
38942             maxDate : this.maxValue,
38943             disabledDatesRE : this.ddMatch,
38944             disabledDatesText : this.disabledDatesText,
38945             
38946             format : this.useIso ? 'Y-m-d' : this.format,
38947             minText : String.format(this.minText, this.formatDate(this.minValue)),
38948             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38949             
38950         });
38951          this.menu.on(Roo.apply({}, this.menuListeners, {
38952             scope:this
38953         }));
38954        
38955         
38956         var m = this.menu;
38957         var p = m.picker;
38958         
38959         // hide month picker get's called when we called by 'before hide';
38960         
38961         var ignorehide = true;
38962         p.hideMonthPicker  = function(disableAnim){
38963             if (ignorehide) {
38964                 return;
38965             }
38966              if(this.monthPicker){
38967                 Roo.log("hideMonthPicker called");
38968                 if(disableAnim === true){
38969                     this.monthPicker.hide();
38970                 }else{
38971                     this.monthPicker.slideOut('t', {duration:.2});
38972                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38973                     p.fireEvent("select", this, this.value);
38974                     m.hide();
38975                 }
38976             }
38977         }
38978         
38979         Roo.log('picker set value');
38980         Roo.log(this.getValue());
38981         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38982         m.show(this.el, 'tl-bl?');
38983         ignorehide  = false;
38984         // this will trigger hideMonthPicker..
38985         
38986         
38987         // hidden the day picker
38988         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38989         
38990         
38991         
38992       
38993         
38994         p.showMonthPicker.defer(100, p);
38995     
38996         
38997        
38998     },
38999
39000     beforeBlur : function(){
39001         var v = this.parseDate(this.getRawValue());
39002         if(v){
39003             this.setValue(v);
39004         }
39005     }
39006
39007     /** @cfg {Boolean} grow @hide */
39008     /** @cfg {Number} growMin @hide */
39009     /** @cfg {Number} growMax @hide */
39010     /**
39011      * @hide
39012      * @method autoSize
39013      */
39014 });/*
39015  * Based on:
39016  * Ext JS Library 1.1.1
39017  * Copyright(c) 2006-2007, Ext JS, LLC.
39018  *
39019  * Originally Released Under LGPL - original licence link has changed is not relivant.
39020  *
39021  * Fork - LGPL
39022  * <script type="text/javascript">
39023  */
39024  
39025
39026 /**
39027  * @class Roo.form.ComboBox
39028  * @extends Roo.form.TriggerField
39029  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39030  * @constructor
39031  * Create a new ComboBox.
39032  * @param {Object} config Configuration options
39033  */
39034 Roo.form.ComboBox = function(config){
39035     Roo.form.ComboBox.superclass.constructor.call(this, config);
39036     this.addEvents({
39037         /**
39038          * @event expand
39039          * Fires when the dropdown list is expanded
39040              * @param {Roo.form.ComboBox} combo This combo box
39041              */
39042         'expand' : true,
39043         /**
39044          * @event collapse
39045          * Fires when the dropdown list is collapsed
39046              * @param {Roo.form.ComboBox} combo This combo box
39047              */
39048         'collapse' : true,
39049         /**
39050          * @event beforeselect
39051          * Fires before a list item is selected. Return false to cancel the selection.
39052              * @param {Roo.form.ComboBox} combo This combo box
39053              * @param {Roo.data.Record} record The data record returned from the underlying store
39054              * @param {Number} index The index of the selected item in the dropdown list
39055              */
39056         'beforeselect' : true,
39057         /**
39058          * @event select
39059          * Fires when a list item is selected
39060              * @param {Roo.form.ComboBox} combo This combo box
39061              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39062              * @param {Number} index The index of the selected item in the dropdown list
39063              */
39064         'select' : true,
39065         /**
39066          * @event beforequery
39067          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39068          * The event object passed has these properties:
39069              * @param {Roo.form.ComboBox} combo This combo box
39070              * @param {String} query The query
39071              * @param {Boolean} forceAll true to force "all" query
39072              * @param {Boolean} cancel true to cancel the query
39073              * @param {Object} e The query event object
39074              */
39075         'beforequery': true,
39076          /**
39077          * @event add
39078          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39079              * @param {Roo.form.ComboBox} combo This combo box
39080              */
39081         'add' : true,
39082         /**
39083          * @event edit
39084          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39085              * @param {Roo.form.ComboBox} combo This combo box
39086              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39087              */
39088         'edit' : true
39089         
39090         
39091     });
39092     if(this.transform){
39093         this.allowDomMove = false;
39094         var s = Roo.getDom(this.transform);
39095         if(!this.hiddenName){
39096             this.hiddenName = s.name;
39097         }
39098         if(!this.store){
39099             this.mode = 'local';
39100             var d = [], opts = s.options;
39101             for(var i = 0, len = opts.length;i < len; i++){
39102                 var o = opts[i];
39103                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39104                 if(o.selected) {
39105                     this.value = value;
39106                 }
39107                 d.push([value, o.text]);
39108             }
39109             this.store = new Roo.data.SimpleStore({
39110                 'id': 0,
39111                 fields: ['value', 'text'],
39112                 data : d
39113             });
39114             this.valueField = 'value';
39115             this.displayField = 'text';
39116         }
39117         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39118         if(!this.lazyRender){
39119             this.target = true;
39120             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39121             s.parentNode.removeChild(s); // remove it
39122             this.render(this.el.parentNode);
39123         }else{
39124             s.parentNode.removeChild(s); // remove it
39125         }
39126
39127     }
39128     if (this.store) {
39129         this.store = Roo.factory(this.store, Roo.data);
39130     }
39131     
39132     this.selectedIndex = -1;
39133     if(this.mode == 'local'){
39134         if(config.queryDelay === undefined){
39135             this.queryDelay = 10;
39136         }
39137         if(config.minChars === undefined){
39138             this.minChars = 0;
39139         }
39140     }
39141 };
39142
39143 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39144     /**
39145      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39146      */
39147     /**
39148      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39149      * rendering into an Roo.Editor, defaults to false)
39150      */
39151     /**
39152      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39153      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39154      */
39155     /**
39156      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39157      */
39158     /**
39159      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39160      * the dropdown list (defaults to undefined, with no header element)
39161      */
39162
39163      /**
39164      * @cfg {String/Roo.Template} tpl The template to use to render the output
39165      */
39166      
39167     // private
39168     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39169     /**
39170      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39171      */
39172     listWidth: undefined,
39173     /**
39174      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39175      * mode = 'remote' or 'text' if mode = 'local')
39176      */
39177     displayField: undefined,
39178     /**
39179      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39180      * mode = 'remote' or 'value' if mode = 'local'). 
39181      * Note: use of a valueField requires the user make a selection
39182      * in order for a value to be mapped.
39183      */
39184     valueField: undefined,
39185     
39186     
39187     /**
39188      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39189      * field's data value (defaults to the underlying DOM element's name)
39190      */
39191     hiddenName: undefined,
39192     /**
39193      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39194      */
39195     listClass: '',
39196     /**
39197      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39198      */
39199     selectedClass: 'x-combo-selected',
39200     /**
39201      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39202      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39203      * which displays a downward arrow icon).
39204      */
39205     triggerClass : 'x-form-arrow-trigger',
39206     /**
39207      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39208      */
39209     shadow:'sides',
39210     /**
39211      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39212      * anchor positions (defaults to 'tl-bl')
39213      */
39214     listAlign: 'tl-bl?',
39215     /**
39216      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39217      */
39218     maxHeight: 300,
39219     /**
39220      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39221      * query specified by the allQuery config option (defaults to 'query')
39222      */
39223     triggerAction: 'query',
39224     /**
39225      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39226      * (defaults to 4, does not apply if editable = false)
39227      */
39228     minChars : 4,
39229     /**
39230      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39231      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39232      */
39233     typeAhead: false,
39234     /**
39235      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39236      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39237      */
39238     queryDelay: 500,
39239     /**
39240      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39241      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39242      */
39243     pageSize: 0,
39244     /**
39245      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39246      * when editable = true (defaults to false)
39247      */
39248     selectOnFocus:false,
39249     /**
39250      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39251      */
39252     queryParam: 'query',
39253     /**
39254      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39255      * when mode = 'remote' (defaults to 'Loading...')
39256      */
39257     loadingText: 'Loading...',
39258     /**
39259      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39260      */
39261     resizable: false,
39262     /**
39263      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39264      */
39265     handleHeight : 8,
39266     /**
39267      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39268      * traditional select (defaults to true)
39269      */
39270     editable: true,
39271     /**
39272      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39273      */
39274     allQuery: '',
39275     /**
39276      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39277      */
39278     mode: 'remote',
39279     /**
39280      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39281      * listWidth has a higher value)
39282      */
39283     minListWidth : 70,
39284     /**
39285      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39286      * allow the user to set arbitrary text into the field (defaults to false)
39287      */
39288     forceSelection:false,
39289     /**
39290      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39291      * if typeAhead = true (defaults to 250)
39292      */
39293     typeAheadDelay : 250,
39294     /**
39295      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39296      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39297      */
39298     valueNotFoundText : undefined,
39299     /**
39300      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39301      */
39302     blockFocus : false,
39303     
39304     /**
39305      * @cfg {Boolean} disableClear Disable showing of clear button.
39306      */
39307     disableClear : false,
39308     /**
39309      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39310      */
39311     alwaysQuery : false,
39312     
39313     //private
39314     addicon : false,
39315     editicon: false,
39316     
39317     // element that contains real text value.. (when hidden is used..)
39318      
39319     // private
39320     onRender : function(ct, position){
39321         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39322         if(this.hiddenName){
39323             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39324                     'before', true);
39325             this.hiddenField.value =
39326                 this.hiddenValue !== undefined ? this.hiddenValue :
39327                 this.value !== undefined ? this.value : '';
39328
39329             // prevent input submission
39330             this.el.dom.removeAttribute('name');
39331              
39332              
39333         }
39334         if(Roo.isGecko){
39335             this.el.dom.setAttribute('autocomplete', 'off');
39336         }
39337
39338         var cls = 'x-combo-list';
39339
39340         this.list = new Roo.Layer({
39341             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39342         });
39343
39344         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39345         this.list.setWidth(lw);
39346         this.list.swallowEvent('mousewheel');
39347         this.assetHeight = 0;
39348
39349         if(this.title){
39350             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39351             this.assetHeight += this.header.getHeight();
39352         }
39353
39354         this.innerList = this.list.createChild({cls:cls+'-inner'});
39355         this.innerList.on('mouseover', this.onViewOver, this);
39356         this.innerList.on('mousemove', this.onViewMove, this);
39357         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39358         
39359         if(this.allowBlank && !this.pageSize && !this.disableClear){
39360             this.footer = this.list.createChild({cls:cls+'-ft'});
39361             this.pageTb = new Roo.Toolbar(this.footer);
39362            
39363         }
39364         if(this.pageSize){
39365             this.footer = this.list.createChild({cls:cls+'-ft'});
39366             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39367                     {pageSize: this.pageSize});
39368             
39369         }
39370         
39371         if (this.pageTb && this.allowBlank && !this.disableClear) {
39372             var _this = this;
39373             this.pageTb.add(new Roo.Toolbar.Fill(), {
39374                 cls: 'x-btn-icon x-btn-clear',
39375                 text: '&#160;',
39376                 handler: function()
39377                 {
39378                     _this.collapse();
39379                     _this.clearValue();
39380                     _this.onSelect(false, -1);
39381                 }
39382             });
39383         }
39384         if (this.footer) {
39385             this.assetHeight += this.footer.getHeight();
39386         }
39387         
39388
39389         if(!this.tpl){
39390             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39391         }
39392
39393         this.view = new Roo.View(this.innerList, this.tpl, {
39394             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39395         });
39396
39397         this.view.on('click', this.onViewClick, this);
39398
39399         this.store.on('beforeload', this.onBeforeLoad, this);
39400         this.store.on('load', this.onLoad, this);
39401         this.store.on('loadexception', this.onLoadException, this);
39402
39403         if(this.resizable){
39404             this.resizer = new Roo.Resizable(this.list,  {
39405                pinned:true, handles:'se'
39406             });
39407             this.resizer.on('resize', function(r, w, h){
39408                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39409                 this.listWidth = w;
39410                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39411                 this.restrictHeight();
39412             }, this);
39413             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39414         }
39415         if(!this.editable){
39416             this.editable = true;
39417             this.setEditable(false);
39418         }  
39419         
39420         
39421         if (typeof(this.events.add.listeners) != 'undefined') {
39422             
39423             this.addicon = this.wrap.createChild(
39424                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39425        
39426             this.addicon.on('click', function(e) {
39427                 this.fireEvent('add', this);
39428             }, this);
39429         }
39430         if (typeof(this.events.edit.listeners) != 'undefined') {
39431             
39432             this.editicon = this.wrap.createChild(
39433                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39434             if (this.addicon) {
39435                 this.editicon.setStyle('margin-left', '40px');
39436             }
39437             this.editicon.on('click', function(e) {
39438                 
39439                 // we fire even  if inothing is selected..
39440                 this.fireEvent('edit', this, this.lastData );
39441                 
39442             }, this);
39443         }
39444         
39445         
39446         
39447     },
39448
39449     // private
39450     initEvents : function(){
39451         Roo.form.ComboBox.superclass.initEvents.call(this);
39452
39453         this.keyNav = new Roo.KeyNav(this.el, {
39454             "up" : function(e){
39455                 this.inKeyMode = true;
39456                 this.selectPrev();
39457             },
39458
39459             "down" : function(e){
39460                 if(!this.isExpanded()){
39461                     this.onTriggerClick();
39462                 }else{
39463                     this.inKeyMode = true;
39464                     this.selectNext();
39465                 }
39466             },
39467
39468             "enter" : function(e){
39469                 this.onViewClick();
39470                 //return true;
39471             },
39472
39473             "esc" : function(e){
39474                 this.collapse();
39475             },
39476
39477             "tab" : function(e){
39478                 this.onViewClick(false);
39479                 this.fireEvent("specialkey", this, e);
39480                 return true;
39481             },
39482
39483             scope : this,
39484
39485             doRelay : function(foo, bar, hname){
39486                 if(hname == 'down' || this.scope.isExpanded()){
39487                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39488                 }
39489                 return true;
39490             },
39491
39492             forceKeyDown: true
39493         });
39494         this.queryDelay = Math.max(this.queryDelay || 10,
39495                 this.mode == 'local' ? 10 : 250);
39496         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39497         if(this.typeAhead){
39498             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39499         }
39500         if(this.editable !== false){
39501             this.el.on("keyup", this.onKeyUp, this);
39502         }
39503         if(this.forceSelection){
39504             this.on('blur', this.doForce, this);
39505         }
39506     },
39507
39508     onDestroy : function(){
39509         if(this.view){
39510             this.view.setStore(null);
39511             this.view.el.removeAllListeners();
39512             this.view.el.remove();
39513             this.view.purgeListeners();
39514         }
39515         if(this.list){
39516             this.list.destroy();
39517         }
39518         if(this.store){
39519             this.store.un('beforeload', this.onBeforeLoad, this);
39520             this.store.un('load', this.onLoad, this);
39521             this.store.un('loadexception', this.onLoadException, this);
39522         }
39523         Roo.form.ComboBox.superclass.onDestroy.call(this);
39524     },
39525
39526     // private
39527     fireKey : function(e){
39528         if(e.isNavKeyPress() && !this.list.isVisible()){
39529             this.fireEvent("specialkey", this, e);
39530         }
39531     },
39532
39533     // private
39534     onResize: function(w, h){
39535         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39536         
39537         if(typeof w != 'number'){
39538             // we do not handle it!?!?
39539             return;
39540         }
39541         var tw = this.trigger.getWidth();
39542         tw += this.addicon ? this.addicon.getWidth() : 0;
39543         tw += this.editicon ? this.editicon.getWidth() : 0;
39544         var x = w - tw;
39545         this.el.setWidth( this.adjustWidth('input', x));
39546             
39547         this.trigger.setStyle('left', x+'px');
39548         
39549         if(this.list && this.listWidth === undefined){
39550             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39551             this.list.setWidth(lw);
39552             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39553         }
39554         
39555     
39556         
39557     },
39558
39559     /**
39560      * Allow or prevent the user from directly editing the field text.  If false is passed,
39561      * the user will only be able to select from the items defined in the dropdown list.  This method
39562      * is the runtime equivalent of setting the 'editable' config option at config time.
39563      * @param {Boolean} value True to allow the user to directly edit the field text
39564      */
39565     setEditable : function(value){
39566         if(value == this.editable){
39567             return;
39568         }
39569         this.editable = value;
39570         if(!value){
39571             this.el.dom.setAttribute('readOnly', true);
39572             this.el.on('mousedown', this.onTriggerClick,  this);
39573             this.el.addClass('x-combo-noedit');
39574         }else{
39575             this.el.dom.setAttribute('readOnly', false);
39576             this.el.un('mousedown', this.onTriggerClick,  this);
39577             this.el.removeClass('x-combo-noedit');
39578         }
39579     },
39580
39581     // private
39582     onBeforeLoad : function(){
39583         if(!this.hasFocus){
39584             return;
39585         }
39586         this.innerList.update(this.loadingText ?
39587                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39588         this.restrictHeight();
39589         this.selectedIndex = -1;
39590     },
39591
39592     // private
39593     onLoad : function(){
39594         if(!this.hasFocus){
39595             return;
39596         }
39597         if(this.store.getCount() > 0){
39598             this.expand();
39599             this.restrictHeight();
39600             if(this.lastQuery == this.allQuery){
39601                 if(this.editable){
39602                     this.el.dom.select();
39603                 }
39604                 if(!this.selectByValue(this.value, true)){
39605                     this.select(0, true);
39606                 }
39607             }else{
39608                 this.selectNext();
39609                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39610                     this.taTask.delay(this.typeAheadDelay);
39611                 }
39612             }
39613         }else{
39614             this.onEmptyResults();
39615         }
39616         //this.el.focus();
39617     },
39618     // private
39619     onLoadException : function()
39620     {
39621         this.collapse();
39622         Roo.log(this.store.reader.jsonData);
39623         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39624             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39625         }
39626         
39627         
39628     },
39629     // private
39630     onTypeAhead : function(){
39631         if(this.store.getCount() > 0){
39632             var r = this.store.getAt(0);
39633             var newValue = r.data[this.displayField];
39634             var len = newValue.length;
39635             var selStart = this.getRawValue().length;
39636             if(selStart != len){
39637                 this.setRawValue(newValue);
39638                 this.selectText(selStart, newValue.length);
39639             }
39640         }
39641     },
39642
39643     // private
39644     onSelect : function(record, index){
39645         if(this.fireEvent('beforeselect', this, record, index) !== false){
39646             this.setFromData(index > -1 ? record.data : false);
39647             this.collapse();
39648             this.fireEvent('select', this, record, index);
39649         }
39650     },
39651
39652     /**
39653      * Returns the currently selected field value or empty string if no value is set.
39654      * @return {String} value The selected value
39655      */
39656     getValue : function(){
39657         if(this.valueField){
39658             return typeof this.value != 'undefined' ? this.value : '';
39659         }else{
39660             return Roo.form.ComboBox.superclass.getValue.call(this);
39661         }
39662     },
39663
39664     /**
39665      * Clears any text/value currently set in the field
39666      */
39667     clearValue : function(){
39668         if(this.hiddenField){
39669             this.hiddenField.value = '';
39670         }
39671         this.value = '';
39672         this.setRawValue('');
39673         this.lastSelectionText = '';
39674         
39675     },
39676
39677     /**
39678      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39679      * will be displayed in the field.  If the value does not match the data value of an existing item,
39680      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39681      * Otherwise the field will be blank (although the value will still be set).
39682      * @param {String} value The value to match
39683      */
39684     setValue : function(v){
39685         var text = v;
39686         if(this.valueField){
39687             var r = this.findRecord(this.valueField, v);
39688             if(r){
39689                 text = r.data[this.displayField];
39690             }else if(this.valueNotFoundText !== undefined){
39691                 text = this.valueNotFoundText;
39692             }
39693         }
39694         this.lastSelectionText = text;
39695         if(this.hiddenField){
39696             this.hiddenField.value = v;
39697         }
39698         Roo.form.ComboBox.superclass.setValue.call(this, text);
39699         this.value = v;
39700     },
39701     /**
39702      * @property {Object} the last set data for the element
39703      */
39704     
39705     lastData : false,
39706     /**
39707      * Sets the value of the field based on a object which is related to the record format for the store.
39708      * @param {Object} value the value to set as. or false on reset?
39709      */
39710     setFromData : function(o){
39711         var dv = ''; // display value
39712         var vv = ''; // value value..
39713         this.lastData = o;
39714         if (this.displayField) {
39715             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39716         } else {
39717             // this is an error condition!!!
39718             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39719         }
39720         
39721         if(this.valueField){
39722             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39723         }
39724         if(this.hiddenField){
39725             this.hiddenField.value = vv;
39726             
39727             this.lastSelectionText = dv;
39728             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39729             this.value = vv;
39730             return;
39731         }
39732         // no hidden field.. - we store the value in 'value', but still display
39733         // display field!!!!
39734         this.lastSelectionText = dv;
39735         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39736         this.value = vv;
39737         
39738         
39739     },
39740     // private
39741     reset : function(){
39742         // overridden so that last data is reset..
39743         this.setValue(this.resetValue);
39744         this.clearInvalid();
39745         this.lastData = false;
39746         if (this.view) {
39747             this.view.clearSelections();
39748         }
39749     },
39750     // private
39751     findRecord : function(prop, value){
39752         var record;
39753         if(this.store.getCount() > 0){
39754             this.store.each(function(r){
39755                 if(r.data[prop] == value){
39756                     record = r;
39757                     return false;
39758                 }
39759                 return true;
39760             });
39761         }
39762         return record;
39763     },
39764     
39765     getName: function()
39766     {
39767         // returns hidden if it's set..
39768         if (!this.rendered) {return ''};
39769         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39770         
39771     },
39772     // private
39773     onViewMove : function(e, t){
39774         this.inKeyMode = false;
39775     },
39776
39777     // private
39778     onViewOver : function(e, t){
39779         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39780             return;
39781         }
39782         var item = this.view.findItemFromChild(t);
39783         if(item){
39784             var index = this.view.indexOf(item);
39785             this.select(index, false);
39786         }
39787     },
39788
39789     // private
39790     onViewClick : function(doFocus)
39791     {
39792         var index = this.view.getSelectedIndexes()[0];
39793         var r = this.store.getAt(index);
39794         if(r){
39795             this.onSelect(r, index);
39796         }
39797         if(doFocus !== false && !this.blockFocus){
39798             this.el.focus();
39799         }
39800     },
39801
39802     // private
39803     restrictHeight : function(){
39804         this.innerList.dom.style.height = '';
39805         var inner = this.innerList.dom;
39806         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39807         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39808         this.list.beginUpdate();
39809         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39810         this.list.alignTo(this.el, this.listAlign);
39811         this.list.endUpdate();
39812     },
39813
39814     // private
39815     onEmptyResults : function(){
39816         this.collapse();
39817     },
39818
39819     /**
39820      * Returns true if the dropdown list is expanded, else false.
39821      */
39822     isExpanded : function(){
39823         return this.list.isVisible();
39824     },
39825
39826     /**
39827      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39828      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39829      * @param {String} value The data value of the item to select
39830      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39831      * selected item if it is not currently in view (defaults to true)
39832      * @return {Boolean} True if the value matched an item in the list, else false
39833      */
39834     selectByValue : function(v, scrollIntoView){
39835         if(v !== undefined && v !== null){
39836             var r = this.findRecord(this.valueField || this.displayField, v);
39837             if(r){
39838                 this.select(this.store.indexOf(r), scrollIntoView);
39839                 return true;
39840             }
39841         }
39842         return false;
39843     },
39844
39845     /**
39846      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39847      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39848      * @param {Number} index The zero-based index of the list item to select
39849      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39850      * selected item if it is not currently in view (defaults to true)
39851      */
39852     select : function(index, scrollIntoView){
39853         this.selectedIndex = index;
39854         this.view.select(index);
39855         if(scrollIntoView !== false){
39856             var el = this.view.getNode(index);
39857             if(el){
39858                 this.innerList.scrollChildIntoView(el, false);
39859             }
39860         }
39861     },
39862
39863     // private
39864     selectNext : function(){
39865         var ct = this.store.getCount();
39866         if(ct > 0){
39867             if(this.selectedIndex == -1){
39868                 this.select(0);
39869             }else if(this.selectedIndex < ct-1){
39870                 this.select(this.selectedIndex+1);
39871             }
39872         }
39873     },
39874
39875     // private
39876     selectPrev : function(){
39877         var ct = this.store.getCount();
39878         if(ct > 0){
39879             if(this.selectedIndex == -1){
39880                 this.select(0);
39881             }else if(this.selectedIndex != 0){
39882                 this.select(this.selectedIndex-1);
39883             }
39884         }
39885     },
39886
39887     // private
39888     onKeyUp : function(e){
39889         if(this.editable !== false && !e.isSpecialKey()){
39890             this.lastKey = e.getKey();
39891             this.dqTask.delay(this.queryDelay);
39892         }
39893     },
39894
39895     // private
39896     validateBlur : function(){
39897         return !this.list || !this.list.isVisible();   
39898     },
39899
39900     // private
39901     initQuery : function(){
39902         this.doQuery(this.getRawValue());
39903     },
39904
39905     // private
39906     doForce : function(){
39907         if(this.el.dom.value.length > 0){
39908             this.el.dom.value =
39909                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39910              
39911         }
39912     },
39913
39914     /**
39915      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39916      * query allowing the query action to be canceled if needed.
39917      * @param {String} query The SQL query to execute
39918      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39919      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39920      * saved in the current store (defaults to false)
39921      */
39922     doQuery : function(q, forceAll){
39923         if(q === undefined || q === null){
39924             q = '';
39925         }
39926         var qe = {
39927             query: q,
39928             forceAll: forceAll,
39929             combo: this,
39930             cancel:false
39931         };
39932         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39933             return false;
39934         }
39935         q = qe.query;
39936         forceAll = qe.forceAll;
39937         if(forceAll === true || (q.length >= this.minChars)){
39938             if(this.lastQuery != q || this.alwaysQuery){
39939                 this.lastQuery = q;
39940                 if(this.mode == 'local'){
39941                     this.selectedIndex = -1;
39942                     if(forceAll){
39943                         this.store.clearFilter();
39944                     }else{
39945                         this.store.filter(this.displayField, q);
39946                     }
39947                     this.onLoad();
39948                 }else{
39949                     this.store.baseParams[this.queryParam] = q;
39950                     this.store.load({
39951                         params: this.getParams(q)
39952                     });
39953                     this.expand();
39954                 }
39955             }else{
39956                 this.selectedIndex = -1;
39957                 this.onLoad();   
39958             }
39959         }
39960     },
39961
39962     // private
39963     getParams : function(q){
39964         var p = {};
39965         //p[this.queryParam] = q;
39966         if(this.pageSize){
39967             p.start = 0;
39968             p.limit = this.pageSize;
39969         }
39970         return p;
39971     },
39972
39973     /**
39974      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39975      */
39976     collapse : function(){
39977         if(!this.isExpanded()){
39978             return;
39979         }
39980         this.list.hide();
39981         Roo.get(document).un('mousedown', this.collapseIf, this);
39982         Roo.get(document).un('mousewheel', this.collapseIf, this);
39983         if (!this.editable) {
39984             Roo.get(document).un('keydown', this.listKeyPress, this);
39985         }
39986         this.fireEvent('collapse', this);
39987     },
39988
39989     // private
39990     collapseIf : function(e){
39991         if(!e.within(this.wrap) && !e.within(this.list)){
39992             this.collapse();
39993         }
39994     },
39995
39996     /**
39997      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39998      */
39999     expand : function(){
40000         if(this.isExpanded() || !this.hasFocus){
40001             return;
40002         }
40003         this.list.alignTo(this.el, this.listAlign);
40004         this.list.show();
40005         Roo.get(document).on('mousedown', this.collapseIf, this);
40006         Roo.get(document).on('mousewheel', this.collapseIf, this);
40007         if (!this.editable) {
40008             Roo.get(document).on('keydown', this.listKeyPress, this);
40009         }
40010         
40011         this.fireEvent('expand', this);
40012     },
40013
40014     // private
40015     // Implements the default empty TriggerField.onTriggerClick function
40016     onTriggerClick : function(){
40017         if(this.disabled){
40018             return;
40019         }
40020         if(this.isExpanded()){
40021             this.collapse();
40022             if (!this.blockFocus) {
40023                 this.el.focus();
40024             }
40025             
40026         }else {
40027             this.hasFocus = true;
40028             if(this.triggerAction == 'all') {
40029                 this.doQuery(this.allQuery, true);
40030             } else {
40031                 this.doQuery(this.getRawValue());
40032             }
40033             if (!this.blockFocus) {
40034                 this.el.focus();
40035             }
40036         }
40037     },
40038     listKeyPress : function(e)
40039     {
40040         //Roo.log('listkeypress');
40041         // scroll to first matching element based on key pres..
40042         if (e.isSpecialKey()) {
40043             return false;
40044         }
40045         var k = String.fromCharCode(e.getKey()).toUpperCase();
40046         //Roo.log(k);
40047         var match  = false;
40048         var csel = this.view.getSelectedNodes();
40049         var cselitem = false;
40050         if (csel.length) {
40051             var ix = this.view.indexOf(csel[0]);
40052             cselitem  = this.store.getAt(ix);
40053             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40054                 cselitem = false;
40055             }
40056             
40057         }
40058         
40059         this.store.each(function(v) { 
40060             if (cselitem) {
40061                 // start at existing selection.
40062                 if (cselitem.id == v.id) {
40063                     cselitem = false;
40064                 }
40065                 return;
40066             }
40067                 
40068             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40069                 match = this.store.indexOf(v);
40070                 return false;
40071             }
40072         }, this);
40073         
40074         if (match === false) {
40075             return true; // no more action?
40076         }
40077         // scroll to?
40078         this.view.select(match);
40079         var sn = Roo.get(this.view.getSelectedNodes()[0])
40080         sn.scrollIntoView(sn.dom.parentNode, false);
40081     }
40082
40083     /** 
40084     * @cfg {Boolean} grow 
40085     * @hide 
40086     */
40087     /** 
40088     * @cfg {Number} growMin 
40089     * @hide 
40090     */
40091     /** 
40092     * @cfg {Number} growMax 
40093     * @hide 
40094     */
40095     /**
40096      * @hide
40097      * @method autoSize
40098      */
40099 });/*
40100  * Copyright(c) 2010-2012, Roo J Solutions Limited
40101  *
40102  * Licence LGPL
40103  *
40104  */
40105
40106 /**
40107  * @class Roo.form.ComboBoxArray
40108  * @extends Roo.form.TextField
40109  * A facebook style adder... for lists of email / people / countries  etc...
40110  * pick multiple items from a combo box, and shows each one.
40111  *
40112  *  Fred [x]  Brian [x]  [Pick another |v]
40113  *
40114  *
40115  *  For this to work: it needs various extra information
40116  *    - normal combo problay has
40117  *      name, hiddenName
40118  *    + displayField, valueField
40119  *
40120  *    For our purpose...
40121  *
40122  *
40123  *   If we change from 'extends' to wrapping...
40124  *   
40125  *  
40126  *
40127  
40128  
40129  * @constructor
40130  * Create a new ComboBoxArray.
40131  * @param {Object} config Configuration options
40132  */
40133  
40134
40135 Roo.form.ComboBoxArray = function(config)
40136 {
40137     
40138     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40139     
40140     this.items = new Roo.util.MixedCollection(false);
40141     
40142     // construct the child combo...
40143     
40144     
40145     
40146     
40147    
40148     
40149 }
40150
40151  
40152 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40153
40154     /**
40155      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40156      */
40157     
40158     lastData : false,
40159     
40160     // behavies liek a hiddne field
40161     inputType:      'hidden',
40162     /**
40163      * @cfg {Number} width The width of the box that displays the selected element
40164      */ 
40165     width:          300,
40166
40167     
40168     
40169     /**
40170      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40171      */
40172     name : false,
40173     /**
40174      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40175      */
40176     hiddenName : false,
40177     
40178     
40179     // private the array of items that are displayed..
40180     items  : false,
40181     // private - the hidden field el.
40182     hiddenEl : false,
40183     // private - the filed el..
40184     el : false,
40185     
40186     //validateValue : function() { return true; }, // all values are ok!
40187     //onAddClick: function() { },
40188     
40189     onRender : function(ct, position) 
40190     {
40191         
40192         // create the standard hidden element
40193         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40194         
40195         
40196         // give fake names to child combo;
40197         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40198         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40199         
40200         this.combo = Roo.factory(this.combo, Roo.form);
40201         this.combo.onRender(ct, position);
40202         if (typeof(this.combo.width) != 'undefined') {
40203             this.combo.onResize(this.combo.width,0);
40204         }
40205         
40206         this.combo.initEvents();
40207         
40208         // assigned so form know we need to do this..
40209         this.store          = this.combo.store;
40210         this.valueField     = this.combo.valueField;
40211         this.displayField   = this.combo.displayField ;
40212         
40213         
40214         this.combo.wrap.addClass('x-cbarray-grp');
40215         
40216         var cbwrap = this.combo.wrap.createChild(
40217             {tag: 'div', cls: 'x-cbarray-cb'},
40218             this.combo.el.dom
40219         );
40220         
40221              
40222         this.hiddenEl = this.combo.wrap.createChild({
40223             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40224         });
40225         this.el = this.combo.wrap.createChild({
40226             tag: 'input',  type:'hidden' , name: this.name, value : ''
40227         });
40228          //   this.el.dom.removeAttribute("name");
40229         
40230         
40231         this.outerWrap = this.combo.wrap;
40232         this.wrap = cbwrap;
40233         
40234         this.outerWrap.setWidth(this.width);
40235         this.outerWrap.dom.removeChild(this.el.dom);
40236         
40237         this.wrap.dom.appendChild(this.el.dom);
40238         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40239         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40240         
40241         this.combo.trigger.setStyle('position','relative');
40242         this.combo.trigger.setStyle('left', '0px');
40243         this.combo.trigger.setStyle('top', '2px');
40244         
40245         this.combo.el.setStyle('vertical-align', 'text-bottom');
40246         
40247         //this.trigger.setStyle('vertical-align', 'top');
40248         
40249         // this should use the code from combo really... on('add' ....)
40250         if (this.adder) {
40251             
40252         
40253             this.adder = this.outerWrap.createChild(
40254                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40255             var _t = this;
40256             this.adder.on('click', function(e) {
40257                 _t.fireEvent('adderclick', this, e);
40258             }, _t);
40259         }
40260         //var _t = this;
40261         //this.adder.on('click', this.onAddClick, _t);
40262         
40263         
40264         this.combo.on('select', function(cb, rec, ix) {
40265             this.addItem(rec.data);
40266             
40267             cb.setValue('');
40268             cb.el.dom.value = '';
40269             //cb.lastData = rec.data;
40270             // add to list
40271             
40272         }, this);
40273         
40274         
40275     },
40276     
40277     
40278     getName: function()
40279     {
40280         // returns hidden if it's set..
40281         if (!this.rendered) {return ''};
40282         return  this.hiddenName ? this.hiddenName : this.name;
40283         
40284     },
40285     
40286     
40287     onResize: function(w, h){
40288         
40289         return;
40290         // not sure if this is needed..
40291         //this.combo.onResize(w,h);
40292         
40293         if(typeof w != 'number'){
40294             // we do not handle it!?!?
40295             return;
40296         }
40297         var tw = this.combo.trigger.getWidth();
40298         tw += this.addicon ? this.addicon.getWidth() : 0;
40299         tw += this.editicon ? this.editicon.getWidth() : 0;
40300         var x = w - tw;
40301         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40302             
40303         this.combo.trigger.setStyle('left', '0px');
40304         
40305         if(this.list && this.listWidth === undefined){
40306             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40307             this.list.setWidth(lw);
40308             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40309         }
40310         
40311     
40312         
40313     },
40314     
40315     addItem: function(rec)
40316     {
40317         var valueField = this.combo.valueField;
40318         var displayField = this.combo.displayField;
40319         if (this.items.indexOfKey(rec[valueField]) > -1) {
40320             //console.log("GOT " + rec.data.id);
40321             return;
40322         }
40323         
40324         var x = new Roo.form.ComboBoxArray.Item({
40325             //id : rec[this.idField],
40326             data : rec,
40327             displayField : displayField ,
40328             tipField : displayField ,
40329             cb : this
40330         });
40331         // use the 
40332         this.items.add(rec[valueField],x);
40333         // add it before the element..
40334         this.updateHiddenEl();
40335         x.render(this.outerWrap, this.wrap.dom);
40336         // add the image handler..
40337     },
40338     
40339     updateHiddenEl : function()
40340     {
40341         this.validate();
40342         if (!this.hiddenEl) {
40343             return;
40344         }
40345         var ar = [];
40346         var idField = this.combo.valueField;
40347         
40348         this.items.each(function(f) {
40349             ar.push(f.data[idField]);
40350            
40351         });
40352         this.hiddenEl.dom.value = ar.join(',');
40353         this.validate();
40354     },
40355     
40356     reset : function()
40357     {
40358         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40359         this.items.each(function(f) {
40360            f.remove(); 
40361         });
40362         this.el.dom.value = '';
40363         if (this.hiddenEl) {
40364             this.hiddenEl.dom.value = '';
40365         }
40366         
40367     },
40368     getValue: function()
40369     {
40370         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40371     },
40372     setValue: function(v) // not a valid action - must use addItems..
40373     {
40374          
40375         this.reset();
40376         
40377         
40378         
40379         if (this.store.isLocal && (typeof(v) == 'string')) {
40380             // then we can use the store to find the values..
40381             // comma seperated at present.. this needs to allow JSON based encoding..
40382             this.hiddenEl.value  = v;
40383             var v_ar = [];
40384             Roo.each(v.split(','), function(k) {
40385                 Roo.log("CHECK " + this.valueField + ',' + k);
40386                 var li = this.store.query(this.valueField, k);
40387                 if (!li.length) {
40388                     return;
40389                 }
40390                 var add = {};
40391                 add[this.valueField] = k;
40392                 add[this.displayField] = li.item(0).data[this.displayField];
40393                 
40394                 this.addItem(add);
40395             }, this) 
40396              
40397         }
40398         if (typeof(v) == 'object') {
40399             // then let's assume it's an array of objects..
40400             Roo.each(v, function(l) {
40401                 this.addItem(l);
40402             }, this);
40403              
40404         }
40405         
40406         
40407     },
40408     setFromData: function(v)
40409     {
40410         // this recieves an object, if setValues is called.
40411         this.reset();
40412         this.el.dom.value = v[this.displayField];
40413         this.hiddenEl.dom.value = v[this.valueField];
40414         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40415             return;
40416         }
40417         var kv = v[this.valueField];
40418         var dv = v[this.displayField];
40419         kv = typeof(kv) != 'string' ? '' : kv;
40420         dv = typeof(dv) != 'string' ? '' : dv;
40421         
40422         
40423         var keys = kv.split(',');
40424         var display = dv.split(',');
40425         for (var i = 0 ; i < keys.length; i++) {
40426             
40427             add = {};
40428             add[this.valueField] = keys[i];
40429             add[this.displayField] = display[i];
40430             this.addItem(add);
40431         }
40432       
40433         
40434     },
40435     
40436     /**
40437      * Validates the combox array value
40438      * @return {Boolean} True if the value is valid, else false
40439      */
40440     validate : function(){
40441         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40442             this.clearInvalid();
40443             return true;
40444         }
40445         return false;
40446     },
40447     
40448     validateValue : function(value){
40449         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40450         
40451     },
40452     
40453     /*@
40454      * overide
40455      * 
40456      */
40457     isDirty : function() {
40458         if(this.disabled) {
40459             return false;
40460         }
40461         
40462         try {
40463             var d = Roo.decode(String(this.originalValue));
40464         } catch (e) {
40465             return String(this.getValue()) !== String(this.originalValue);
40466         }
40467         
40468         var originalValue = [];
40469         
40470         for (var i = 0; i < d.length; i++){
40471             originalValue.push(d[i][this.valueField]);
40472         }
40473         
40474         return String(this.getValue()) !== String(originalValue.join(','));
40475         
40476     }
40477     
40478 });
40479
40480
40481
40482 /**
40483  * @class Roo.form.ComboBoxArray.Item
40484  * @extends Roo.BoxComponent
40485  * A selected item in the list
40486  *  Fred [x]  Brian [x]  [Pick another |v]
40487  * 
40488  * @constructor
40489  * Create a new item.
40490  * @param {Object} config Configuration options
40491  */
40492  
40493 Roo.form.ComboBoxArray.Item = function(config) {
40494     config.id = Roo.id();
40495     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40496 }
40497
40498 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40499     data : {},
40500     cb: false,
40501     displayField : false,
40502     tipField : false,
40503     
40504     
40505     defaultAutoCreate : {
40506         tag: 'div',
40507         cls: 'x-cbarray-item',
40508         cn : [ 
40509             { tag: 'div' },
40510             {
40511                 tag: 'img',
40512                 width:16,
40513                 height : 16,
40514                 src : Roo.BLANK_IMAGE_URL ,
40515                 align: 'center'
40516             }
40517         ]
40518         
40519     },
40520     
40521  
40522     onRender : function(ct, position)
40523     {
40524         Roo.form.Field.superclass.onRender.call(this, ct, position);
40525         
40526         if(!this.el){
40527             var cfg = this.getAutoCreate();
40528             this.el = ct.createChild(cfg, position);
40529         }
40530         
40531         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40532         
40533         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40534             this.cb.renderer(this.data) :
40535             String.format('{0}',this.data[this.displayField]);
40536         
40537             
40538         this.el.child('div').dom.setAttribute('qtip',
40539                         String.format('{0}',this.data[this.tipField])
40540         );
40541         
40542         this.el.child('img').on('click', this.remove, this);
40543         
40544     },
40545    
40546     remove : function()
40547     {
40548         
40549         this.cb.items.remove(this);
40550         this.el.child('img').un('click', this.remove, this);
40551         this.el.remove();
40552         this.cb.updateHiddenEl();
40553     }
40554 });/*
40555  * Based on:
40556  * Ext JS Library 1.1.1
40557  * Copyright(c) 2006-2007, Ext JS, LLC.
40558  *
40559  * Originally Released Under LGPL - original licence link has changed is not relivant.
40560  *
40561  * Fork - LGPL
40562  * <script type="text/javascript">
40563  */
40564 /**
40565  * @class Roo.form.Checkbox
40566  * @extends Roo.form.Field
40567  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40568  * @constructor
40569  * Creates a new Checkbox
40570  * @param {Object} config Configuration options
40571  */
40572 Roo.form.Checkbox = function(config){
40573     Roo.form.Checkbox.superclass.constructor.call(this, config);
40574     this.addEvents({
40575         /**
40576          * @event check
40577          * Fires when the checkbox is checked or unchecked.
40578              * @param {Roo.form.Checkbox} this This checkbox
40579              * @param {Boolean} checked The new checked value
40580              */
40581         check : true
40582     });
40583 };
40584
40585 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40586     /**
40587      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40588      */
40589     focusClass : undefined,
40590     /**
40591      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40592      */
40593     fieldClass: "x-form-field",
40594     /**
40595      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40596      */
40597     checked: false,
40598     /**
40599      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40600      * {tag: "input", type: "checkbox", autocomplete: "off"})
40601      */
40602     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40603     /**
40604      * @cfg {String} boxLabel The text that appears beside the checkbox
40605      */
40606     boxLabel : "",
40607     /**
40608      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40609      */  
40610     inputValue : '1',
40611     /**
40612      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40613      */
40614      valueOff: '0', // value when not checked..
40615
40616     actionMode : 'viewEl', 
40617     //
40618     // private
40619     itemCls : 'x-menu-check-item x-form-item',
40620     groupClass : 'x-menu-group-item',
40621     inputType : 'hidden',
40622     
40623     
40624     inSetChecked: false, // check that we are not calling self...
40625     
40626     inputElement: false, // real input element?
40627     basedOn: false, // ????
40628     
40629     isFormField: true, // not sure where this is needed!!!!
40630
40631     onResize : function(){
40632         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40633         if(!this.boxLabel){
40634             this.el.alignTo(this.wrap, 'c-c');
40635         }
40636     },
40637
40638     initEvents : function(){
40639         Roo.form.Checkbox.superclass.initEvents.call(this);
40640         this.el.on("click", this.onClick,  this);
40641         this.el.on("change", this.onClick,  this);
40642     },
40643
40644
40645     getResizeEl : function(){
40646         return this.wrap;
40647     },
40648
40649     getPositionEl : function(){
40650         return this.wrap;
40651     },
40652
40653     // private
40654     onRender : function(ct, position){
40655         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40656         /*
40657         if(this.inputValue !== undefined){
40658             this.el.dom.value = this.inputValue;
40659         }
40660         */
40661         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40662         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40663         var viewEl = this.wrap.createChild({ 
40664             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40665         this.viewEl = viewEl;   
40666         this.wrap.on('click', this.onClick,  this); 
40667         
40668         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40669         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40670         
40671         
40672         
40673         if(this.boxLabel){
40674             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40675         //    viewEl.on('click', this.onClick,  this); 
40676         }
40677         //if(this.checked){
40678             this.setChecked(this.checked);
40679         //}else{
40680             //this.checked = this.el.dom;
40681         //}
40682
40683     },
40684
40685     // private
40686     initValue : Roo.emptyFn,
40687
40688     /**
40689      * Returns the checked state of the checkbox.
40690      * @return {Boolean} True if checked, else false
40691      */
40692     getValue : function(){
40693         if(this.el){
40694             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40695         }
40696         return this.valueOff;
40697         
40698     },
40699
40700         // private
40701     onClick : function(){ 
40702         this.setChecked(!this.checked);
40703
40704         //if(this.el.dom.checked != this.checked){
40705         //    this.setValue(this.el.dom.checked);
40706        // }
40707     },
40708
40709     /**
40710      * Sets the checked state of the checkbox.
40711      * On is always based on a string comparison between inputValue and the param.
40712      * @param {Boolean/String} value - the value to set 
40713      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40714      */
40715     setValue : function(v,suppressEvent){
40716         
40717         
40718         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40719         //if(this.el && this.el.dom){
40720         //    this.el.dom.checked = this.checked;
40721         //    this.el.dom.defaultChecked = this.checked;
40722         //}
40723         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40724         //this.fireEvent("check", this, this.checked);
40725     },
40726     // private..
40727     setChecked : function(state,suppressEvent)
40728     {
40729         if (this.inSetChecked) {
40730             this.checked = state;
40731             return;
40732         }
40733         
40734     
40735         if(this.wrap){
40736             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40737         }
40738         this.checked = state;
40739         if(suppressEvent !== true){
40740             this.fireEvent('check', this, state);
40741         }
40742         this.inSetChecked = true;
40743         this.el.dom.value = state ? this.inputValue : this.valueOff;
40744         this.inSetChecked = false;
40745         
40746     },
40747     // handle setting of hidden value by some other method!!?!?
40748     setFromHidden: function()
40749     {
40750         if(!this.el){
40751             return;
40752         }
40753         //console.log("SET FROM HIDDEN");
40754         //alert('setFrom hidden');
40755         this.setValue(this.el.dom.value);
40756     },
40757     
40758     onDestroy : function()
40759     {
40760         if(this.viewEl){
40761             Roo.get(this.viewEl).remove();
40762         }
40763          
40764         Roo.form.Checkbox.superclass.onDestroy.call(this);
40765     }
40766
40767 });/*
40768  * Based on:
40769  * Ext JS Library 1.1.1
40770  * Copyright(c) 2006-2007, Ext JS, LLC.
40771  *
40772  * Originally Released Under LGPL - original licence link has changed is not relivant.
40773  *
40774  * Fork - LGPL
40775  * <script type="text/javascript">
40776  */
40777  
40778 /**
40779  * @class Roo.form.Radio
40780  * @extends Roo.form.Checkbox
40781  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40782  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40783  * @constructor
40784  * Creates a new Radio
40785  * @param {Object} config Configuration options
40786  */
40787 Roo.form.Radio = function(){
40788     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40789 };
40790 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40791     inputType: 'radio',
40792
40793     /**
40794      * If this radio is part of a group, it will return the selected value
40795      * @return {String}
40796      */
40797     getGroupValue : function(){
40798         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40799     },
40800     
40801     
40802     onRender : function(ct, position){
40803         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40804         
40805         if(this.inputValue !== undefined){
40806             this.el.dom.value = this.inputValue;
40807         }
40808          
40809         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40810         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40811         //var viewEl = this.wrap.createChild({ 
40812         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40813         //this.viewEl = viewEl;   
40814         //this.wrap.on('click', this.onClick,  this); 
40815         
40816         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40817         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40818         
40819         
40820         
40821         if(this.boxLabel){
40822             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40823         //    viewEl.on('click', this.onClick,  this); 
40824         }
40825          if(this.checked){
40826             this.el.dom.checked =   'checked' ;
40827         }
40828          
40829     } 
40830     
40831     
40832 });//<script type="text/javascript">
40833
40834 /*
40835  * Ext JS Library 1.1.1
40836  * Copyright(c) 2006-2007, Ext JS, LLC.
40837  * licensing@extjs.com
40838  * 
40839  * http://www.extjs.com/license
40840  */
40841  
40842  /*
40843   * 
40844   * Known bugs:
40845   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40846   * - IE ? - no idea how much works there.
40847   * 
40848   * 
40849   * 
40850   */
40851  
40852
40853 /**
40854  * @class Ext.form.HtmlEditor
40855  * @extends Ext.form.Field
40856  * Provides a lightweight HTML Editor component.
40857  *
40858  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40859  * 
40860  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40861  * supported by this editor.</b><br/><br/>
40862  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40863  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40864  */
40865 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40866       /**
40867      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40868      */
40869     toolbars : false,
40870     /**
40871      * @cfg {String} createLinkText The default text for the create link prompt
40872      */
40873     createLinkText : 'Please enter the URL for the link:',
40874     /**
40875      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40876      */
40877     defaultLinkValue : 'http:/'+'/',
40878    
40879      /**
40880      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40881      *                        Roo.resizable.
40882      */
40883     resizable : false,
40884      /**
40885      * @cfg {Number} height (in pixels)
40886      */   
40887     height: 300,
40888    /**
40889      * @cfg {Number} width (in pixels)
40890      */   
40891     width: 500,
40892     
40893     /**
40894      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40895      * 
40896      */
40897     stylesheets: false,
40898     
40899     // id of frame..
40900     frameId: false,
40901     
40902     // private properties
40903     validationEvent : false,
40904     deferHeight: true,
40905     initialized : false,
40906     activated : false,
40907     sourceEditMode : false,
40908     onFocus : Roo.emptyFn,
40909     iframePad:3,
40910     hideMode:'offsets',
40911     
40912     defaultAutoCreate : { // modified by initCompnoent..
40913         tag: "textarea",
40914         style:"width:500px;height:300px;",
40915         autocomplete: "off"
40916     },
40917
40918     // private
40919     initComponent : function(){
40920         this.addEvents({
40921             /**
40922              * @event initialize
40923              * Fires when the editor is fully initialized (including the iframe)
40924              * @param {HtmlEditor} this
40925              */
40926             initialize: true,
40927             /**
40928              * @event activate
40929              * Fires when the editor is first receives the focus. Any insertion must wait
40930              * until after this event.
40931              * @param {HtmlEditor} this
40932              */
40933             activate: true,
40934              /**
40935              * @event beforesync
40936              * Fires before the textarea is updated with content from the editor iframe. Return false
40937              * to cancel the sync.
40938              * @param {HtmlEditor} this
40939              * @param {String} html
40940              */
40941             beforesync: true,
40942              /**
40943              * @event beforepush
40944              * Fires before the iframe editor is updated with content from the textarea. Return false
40945              * to cancel the push.
40946              * @param {HtmlEditor} this
40947              * @param {String} html
40948              */
40949             beforepush: true,
40950              /**
40951              * @event sync
40952              * Fires when the textarea is updated with content from the editor iframe.
40953              * @param {HtmlEditor} this
40954              * @param {String} html
40955              */
40956             sync: true,
40957              /**
40958              * @event push
40959              * Fires when the iframe editor is updated with content from the textarea.
40960              * @param {HtmlEditor} this
40961              * @param {String} html
40962              */
40963             push: true,
40964              /**
40965              * @event editmodechange
40966              * Fires when the editor switches edit modes
40967              * @param {HtmlEditor} this
40968              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40969              */
40970             editmodechange: true,
40971             /**
40972              * @event editorevent
40973              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40974              * @param {HtmlEditor} this
40975              */
40976             editorevent: true
40977         });
40978         this.defaultAutoCreate =  {
40979             tag: "textarea",
40980             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40981             autocomplete: "off"
40982         };
40983     },
40984
40985     /**
40986      * Protected method that will not generally be called directly. It
40987      * is called when the editor creates its toolbar. Override this method if you need to
40988      * add custom toolbar buttons.
40989      * @param {HtmlEditor} editor
40990      */
40991     createToolbar : function(editor){
40992         if (!editor.toolbars || !editor.toolbars.length) {
40993             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40994         }
40995         
40996         for (var i =0 ; i < editor.toolbars.length;i++) {
40997             editor.toolbars[i] = Roo.factory(
40998                     typeof(editor.toolbars[i]) == 'string' ?
40999                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
41000                 Roo.form.HtmlEditor);
41001             editor.toolbars[i].init(editor);
41002         }
41003          
41004         
41005     },
41006
41007     /**
41008      * Protected method that will not generally be called directly. It
41009      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41010      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41011      */
41012     getDocMarkup : function(){
41013         // body styles..
41014         var st = '';
41015         if (this.stylesheets === false) {
41016             
41017             Roo.get(document.head).select('style').each(function(node) {
41018                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41019             });
41020             
41021             Roo.get(document.head).select('link').each(function(node) { 
41022                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41023             });
41024             
41025         } else if (!this.stylesheets.length) {
41026                 // simple..
41027                 st = '<style type="text/css">' +
41028                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41029                    '</style>';
41030         } else {
41031             Roo.each(this.stylesheets, function(s) {
41032                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41033             });
41034             
41035         }
41036         
41037         st +=  '<style type="text/css">' +
41038             'IMG { cursor: pointer } ' +
41039         '</style>';
41040
41041         
41042         return '<html><head>' + st  +
41043             //<style type="text/css">' +
41044             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41045             //'</style>' +
41046             ' </head><body class="roo-htmleditor-body"></body></html>';
41047     },
41048
41049     // private
41050     onRender : function(ct, position)
41051     {
41052         var _t = this;
41053         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
41054         this.el.dom.style.border = '0 none';
41055         this.el.dom.setAttribute('tabIndex', -1);
41056         this.el.addClass('x-hidden');
41057         if(Roo.isIE){ // fix IE 1px bogus margin
41058             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41059         }
41060         this.wrap = this.el.wrap({
41061             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
41062         });
41063         
41064         if (this.resizable) {
41065             this.resizeEl = new Roo.Resizable(this.wrap, {
41066                 pinned : true,
41067                 wrap: true,
41068                 dynamic : true,
41069                 minHeight : this.height,
41070                 height: this.height,
41071                 handles : this.resizable,
41072                 width: this.width,
41073                 listeners : {
41074                     resize : function(r, w, h) {
41075                         _t.onResize(w,h); // -something
41076                     }
41077                 }
41078             });
41079             
41080         }
41081
41082         this.frameId = Roo.id();
41083         
41084         this.createToolbar(this);
41085         
41086       
41087         
41088         var iframe = this.wrap.createChild({
41089             tag: 'iframe',
41090             id: this.frameId,
41091             name: this.frameId,
41092             frameBorder : 'no',
41093             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41094         }, this.el
41095         );
41096         
41097        // console.log(iframe);
41098         //this.wrap.dom.appendChild(iframe);
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         if(!this.width){
41132             this.setSize(this.wrap.getSize());
41133         }
41134         if (this.resizeEl) {
41135             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
41136             // should trigger onReize..
41137         }
41138     },
41139
41140     // private
41141     onResize : function(w, h)
41142     {
41143         //Roo.log('resize: ' +w + ',' + h );
41144         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
41145         if(this.el && this.iframe){
41146             if(typeof w == 'number'){
41147                 var aw = w - this.wrap.getFrameWidth('lr');
41148                 this.el.setWidth(this.adjustWidth('textarea', aw));
41149                 this.iframe.style.width = aw + 'px';
41150             }
41151             if(typeof h == 'number'){
41152                 var tbh = 0;
41153                 for (var i =0; i < this.toolbars.length;i++) {
41154                     // fixme - ask toolbars for heights?
41155                     tbh += this.toolbars[i].tb.el.getHeight();
41156                     if (this.toolbars[i].footer) {
41157                         tbh += this.toolbars[i].footer.el.getHeight();
41158                     }
41159                 }
41160                 
41161                 
41162                 
41163                 
41164                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
41165                 ah -= 5; // knock a few pixes off for look..
41166                 this.el.setHeight(this.adjustWidth('textarea', ah));
41167                 this.iframe.style.height = ah + 'px';
41168                 if(this.doc){
41169                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
41170                 }
41171             }
41172         }
41173     },
41174
41175     /**
41176      * Toggles the editor between standard and source edit mode.
41177      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41178      */
41179     toggleSourceEdit : function(sourceEditMode){
41180         
41181         this.sourceEditMode = sourceEditMode === true;
41182         
41183         if(this.sourceEditMode){
41184 //            Roo.log('in');
41185 //            Roo.log(this.syncValue());
41186             this.syncValue();
41187             this.iframe.className = 'x-hidden';
41188             this.el.removeClass('x-hidden');
41189             this.el.dom.removeAttribute('tabIndex');
41190             this.el.focus();
41191         }else{
41192 //            Roo.log('out')
41193 //            Roo.log(this.pushValue()); 
41194             this.pushValue();
41195             this.iframe.className = '';
41196             this.el.addClass('x-hidden');
41197             this.el.dom.setAttribute('tabIndex', -1);
41198             this.deferFocus();
41199         }
41200         this.setSize(this.wrap.getSize());
41201         this.fireEvent('editmodechange', this, this.sourceEditMode);
41202     },
41203
41204     // private used internally
41205     createLink : function(){
41206         var url = prompt(this.createLinkText, this.defaultLinkValue);
41207         if(url && url != 'http:/'+'/'){
41208             this.relayCmd('createlink', url);
41209         }
41210     },
41211
41212     // private (for BoxComponent)
41213     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41214
41215     // private (for BoxComponent)
41216     getResizeEl : function(){
41217         return this.wrap;
41218     },
41219
41220     // private (for BoxComponent)
41221     getPositionEl : function(){
41222         return this.wrap;
41223     },
41224
41225     // private
41226     initEvents : function(){
41227         this.originalValue = this.getValue();
41228     },
41229
41230     /**
41231      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41232      * @method
41233      */
41234     markInvalid : Roo.emptyFn,
41235     /**
41236      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
41237      * @method
41238      */
41239     clearInvalid : Roo.emptyFn,
41240
41241     setValue : function(v){
41242         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
41243         this.pushValue();
41244     },
41245
41246     /**
41247      * Protected method that will not generally be called directly. If you need/want
41248      * custom HTML cleanup, this is the method you should override.
41249      * @param {String} html The HTML to be cleaned
41250      * return {String} The cleaned HTML
41251      */
41252     cleanHtml : function(html){
41253         html = String(html);
41254         if(html.length > 5){
41255             if(Roo.isSafari){ // strip safari nonsense
41256                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41257             }
41258         }
41259         if(html == '&nbsp;'){
41260             html = '';
41261         }
41262         return html;
41263     },
41264
41265     /**
41266      * Protected method that will not generally be called directly. Syncs the contents
41267      * of the editor iframe with the textarea.
41268      */
41269     syncValue : function(){
41270         if(this.initialized){
41271             var bd = (this.doc.body || this.doc.documentElement);
41272             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41273             var html = bd.innerHTML;
41274             if(Roo.isSafari){
41275                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41276                 var m = bs.match(/text-align:(.*?);/i);
41277                 if(m && m[1]){
41278                     html = '<div style="'+m[0]+'">' + html + '</div>';
41279                 }
41280             }
41281             html = this.cleanHtml(html);
41282             // fix up the special chars.. normaly like back quotes in word...
41283             // however we do not want to do this with chinese..
41284             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41285                 var cc = b.charCodeAt();
41286                 if (
41287                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41288                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41289                     (cc >= 0xf900 && cc < 0xfb00 )
41290                 ) {
41291                         return b;
41292                 }
41293                 return "&#"+cc+";" 
41294             });
41295             if(this.fireEvent('beforesync', this, html) !== false){
41296                 this.el.dom.value = html;
41297                 this.fireEvent('sync', this, html);
41298             }
41299         }
41300     },
41301
41302     /**
41303      * Protected method that will not generally be called directly. Pushes the value of the textarea
41304      * into the iframe editor.
41305      */
41306     pushValue : function(){
41307         if(this.initialized){
41308             var v = this.el.dom.value;
41309             
41310             if(v.length < 1){
41311                 v = '&#160;';
41312             }
41313             
41314             if(this.fireEvent('beforepush', this, v) !== false){
41315                 var d = (this.doc.body || this.doc.documentElement);
41316                 d.innerHTML = v;
41317                 this.cleanUpPaste();
41318                 this.el.dom.value = d.innerHTML;
41319                 this.fireEvent('push', this, v);
41320             }
41321         }
41322     },
41323
41324     // private
41325     deferFocus : function(){
41326         this.focus.defer(10, this);
41327     },
41328
41329     // doc'ed in Field
41330     focus : function(){
41331         if(this.win && !this.sourceEditMode){
41332             this.win.focus();
41333         }else{
41334             this.el.focus();
41335         }
41336     },
41337     
41338     assignDocWin: function()
41339     {
41340         var iframe = this.iframe;
41341         
41342          if(Roo.isIE){
41343             this.doc = iframe.contentWindow.document;
41344             this.win = iframe.contentWindow;
41345         } else {
41346             if (!Roo.get(this.frameId)) {
41347                 return;
41348             }
41349             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41350             this.win = Roo.get(this.frameId).dom.contentWindow;
41351         }
41352     },
41353     
41354     // private
41355     initEditor : function(){
41356         //console.log("INIT EDITOR");
41357         this.assignDocWin();
41358         
41359         
41360         
41361         this.doc.designMode="on";
41362         this.doc.open();
41363         this.doc.write(this.getDocMarkup());
41364         this.doc.close();
41365         
41366         var dbody = (this.doc.body || this.doc.documentElement);
41367         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41368         // this copies styles from the containing element into thsi one..
41369         // not sure why we need all of this..
41370         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41371         ss['background-attachment'] = 'fixed'; // w3c
41372         dbody.bgProperties = 'fixed'; // ie
41373         Roo.DomHelper.applyStyles(dbody, ss);
41374         Roo.EventManager.on(this.doc, {
41375             //'mousedown': this.onEditorEvent,
41376             'mouseup': this.onEditorEvent,
41377             'dblclick': this.onEditorEvent,
41378             'click': this.onEditorEvent,
41379             'keyup': this.onEditorEvent,
41380             buffer:100,
41381             scope: this
41382         });
41383         if(Roo.isGecko){
41384             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41385         }
41386         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41387             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41388         }
41389         this.initialized = true;
41390
41391         this.fireEvent('initialize', this);
41392         this.pushValue();
41393     },
41394
41395     // private
41396     onDestroy : function(){
41397         
41398         
41399         
41400         if(this.rendered){
41401             
41402             for (var i =0; i < this.toolbars.length;i++) {
41403                 // fixme - ask toolbars for heights?
41404                 this.toolbars[i].onDestroy();
41405             }
41406             
41407             this.wrap.dom.innerHTML = '';
41408             this.wrap.remove();
41409         }
41410     },
41411
41412     // private
41413     onFirstFocus : function(){
41414         
41415         this.assignDocWin();
41416         
41417         
41418         this.activated = true;
41419         for (var i =0; i < this.toolbars.length;i++) {
41420             this.toolbars[i].onFirstFocus();
41421         }
41422        
41423         if(Roo.isGecko){ // prevent silly gecko errors
41424             this.win.focus();
41425             var s = this.win.getSelection();
41426             if(!s.focusNode || s.focusNode.nodeType != 3){
41427                 var r = s.getRangeAt(0);
41428                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41429                 r.collapse(true);
41430                 this.deferFocus();
41431             }
41432             try{
41433                 this.execCmd('useCSS', true);
41434                 this.execCmd('styleWithCSS', false);
41435             }catch(e){}
41436         }
41437         this.fireEvent('activate', this);
41438     },
41439
41440     // private
41441     adjustFont: function(btn){
41442         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41443         //if(Roo.isSafari){ // safari
41444         //    adjust *= 2;
41445        // }
41446         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41447         if(Roo.isSafari){ // safari
41448             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41449             v =  (v < 10) ? 10 : v;
41450             v =  (v > 48) ? 48 : v;
41451             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41452             
41453         }
41454         
41455         
41456         v = Math.max(1, v+adjust);
41457         
41458         this.execCmd('FontSize', v  );
41459     },
41460
41461     onEditorEvent : function(e){
41462         this.fireEvent('editorevent', this, e);
41463       //  this.updateToolbar();
41464         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41465     },
41466
41467     insertTag : function(tg)
41468     {
41469         // could be a bit smarter... -> wrap the current selected tRoo..
41470         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41471             
41472             range = this.createRange(this.getSelection());
41473             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41474             wrappingNode.appendChild(range.extractContents());
41475             range.insertNode(wrappingNode);
41476
41477             return;
41478             
41479             
41480             
41481         }
41482         this.execCmd("formatblock",   tg);
41483         
41484     },
41485     
41486     insertText : function(txt)
41487     {
41488         
41489         
41490         var range = this.createRange();
41491         range.deleteContents();
41492                //alert(Sender.getAttribute('label'));
41493                
41494         range.insertNode(this.doc.createTextNode(txt));
41495     } ,
41496     
41497     // private
41498     relayBtnCmd : function(btn){
41499         this.relayCmd(btn.cmd);
41500     },
41501
41502     /**
41503      * Executes a Midas editor command on the editor document and performs necessary focus and
41504      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41505      * @param {String} cmd The Midas command
41506      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41507      */
41508     relayCmd : function(cmd, value){
41509         this.win.focus();
41510         this.execCmd(cmd, value);
41511         this.fireEvent('editorevent', this);
41512         //this.updateToolbar();
41513         this.deferFocus();
41514     },
41515
41516     /**
41517      * Executes a Midas editor command directly on the editor document.
41518      * For visual commands, you should use {@link #relayCmd} instead.
41519      * <b>This should only be called after the editor is initialized.</b>
41520      * @param {String} cmd The Midas command
41521      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41522      */
41523     execCmd : function(cmd, value){
41524         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41525         this.syncValue();
41526     },
41527  
41528  
41529    
41530     /**
41531      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41532      * to insert tRoo.
41533      * @param {String} text | dom node.. 
41534      */
41535     insertAtCursor : function(text)
41536     {
41537         
41538         
41539         
41540         if(!this.activated){
41541             return;
41542         }
41543         /*
41544         if(Roo.isIE){
41545             this.win.focus();
41546             var r = this.doc.selection.createRange();
41547             if(r){
41548                 r.collapse(true);
41549                 r.pasteHTML(text);
41550                 this.syncValue();
41551                 this.deferFocus();
41552             
41553             }
41554             return;
41555         }
41556         */
41557         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41558             this.win.focus();
41559             
41560             
41561             // from jquery ui (MIT licenced)
41562             var range, node;
41563             var win = this.win;
41564             
41565             if (win.getSelection && win.getSelection().getRangeAt) {
41566                 range = win.getSelection().getRangeAt(0);
41567                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41568                 range.insertNode(node);
41569             } else if (win.document.selection && win.document.selection.createRange) {
41570                 // no firefox support
41571                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41572                 win.document.selection.createRange().pasteHTML(txt);
41573             } else {
41574                 // no firefox support
41575                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41576                 this.execCmd('InsertHTML', txt);
41577             } 
41578             
41579             this.syncValue();
41580             
41581             this.deferFocus();
41582         }
41583     },
41584  // private
41585     mozKeyPress : function(e){
41586         if(e.ctrlKey){
41587             var c = e.getCharCode(), cmd;
41588           
41589             if(c > 0){
41590                 c = String.fromCharCode(c).toLowerCase();
41591                 switch(c){
41592                     case 'b':
41593                         cmd = 'bold';
41594                         break;
41595                     case 'i':
41596                         cmd = 'italic';
41597                         break;
41598                     
41599                     case 'u':
41600                         cmd = 'underline';
41601                         break;
41602                     
41603                     case 'v':
41604                         this.cleanUpPaste.defer(100, this);
41605                         return;
41606                         
41607                 }
41608                 if(cmd){
41609                     this.win.focus();
41610                     this.execCmd(cmd);
41611                     this.deferFocus();
41612                     e.preventDefault();
41613                 }
41614                 
41615             }
41616         }
41617     },
41618
41619     // private
41620     fixKeys : function(){ // load time branching for fastest keydown performance
41621         if(Roo.isIE){
41622             return function(e){
41623                 var k = e.getKey(), r;
41624                 if(k == e.TAB){
41625                     e.stopEvent();
41626                     r = this.doc.selection.createRange();
41627                     if(r){
41628                         r.collapse(true);
41629                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41630                         this.deferFocus();
41631                     }
41632                     return;
41633                 }
41634                 
41635                 if(k == e.ENTER){
41636                     r = this.doc.selection.createRange();
41637                     if(r){
41638                         var target = r.parentElement();
41639                         if(!target || target.tagName.toLowerCase() != 'li'){
41640                             e.stopEvent();
41641                             r.pasteHTML('<br />');
41642                             r.collapse(false);
41643                             r.select();
41644                         }
41645                     }
41646                 }
41647                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41648                     this.cleanUpPaste.defer(100, this);
41649                     return;
41650                 }
41651                 
41652                 
41653             };
41654         }else if(Roo.isOpera){
41655             return function(e){
41656                 var k = e.getKey();
41657                 if(k == e.TAB){
41658                     e.stopEvent();
41659                     this.win.focus();
41660                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41661                     this.deferFocus();
41662                 }
41663                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41664                     this.cleanUpPaste.defer(100, this);
41665                     return;
41666                 }
41667                 
41668             };
41669         }else if(Roo.isSafari){
41670             return function(e){
41671                 var k = e.getKey();
41672                 
41673                 if(k == e.TAB){
41674                     e.stopEvent();
41675                     this.execCmd('InsertText','\t');
41676                     this.deferFocus();
41677                     return;
41678                 }
41679                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41680                     this.cleanUpPaste.defer(100, this);
41681                     return;
41682                 }
41683                 
41684              };
41685         }
41686     }(),
41687     
41688     getAllAncestors: function()
41689     {
41690         var p = this.getSelectedNode();
41691         var a = [];
41692         if (!p) {
41693             a.push(p); // push blank onto stack..
41694             p = this.getParentElement();
41695         }
41696         
41697         
41698         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41699             a.push(p);
41700             p = p.parentNode;
41701         }
41702         a.push(this.doc.body);
41703         return a;
41704     },
41705     lastSel : false,
41706     lastSelNode : false,
41707     
41708     
41709     getSelection : function() 
41710     {
41711         this.assignDocWin();
41712         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41713     },
41714     
41715     getSelectedNode: function() 
41716     {
41717         // this may only work on Gecko!!!
41718         
41719         // should we cache this!!!!
41720         
41721         
41722         
41723          
41724         var range = this.createRange(this.getSelection()).cloneRange();
41725         
41726         if (Roo.isIE) {
41727             var parent = range.parentElement();
41728             while (true) {
41729                 var testRange = range.duplicate();
41730                 testRange.moveToElementText(parent);
41731                 if (testRange.inRange(range)) {
41732                     break;
41733                 }
41734                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41735                     break;
41736                 }
41737                 parent = parent.parentElement;
41738             }
41739             return parent;
41740         }
41741         
41742         // is ancestor a text element.
41743         var ac =  range.commonAncestorContainer;
41744         if (ac.nodeType == 3) {
41745             ac = ac.parentNode;
41746         }
41747         
41748         var ar = ac.childNodes;
41749          
41750         var nodes = [];
41751         var other_nodes = [];
41752         var has_other_nodes = false;
41753         for (var i=0;i<ar.length;i++) {
41754             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41755                 continue;
41756             }
41757             // fullly contained node.
41758             
41759             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41760                 nodes.push(ar[i]);
41761                 continue;
41762             }
41763             
41764             // probably selected..
41765             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41766                 other_nodes.push(ar[i]);
41767                 continue;
41768             }
41769             // outer..
41770             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41771                 continue;
41772             }
41773             
41774             
41775             has_other_nodes = true;
41776         }
41777         if (!nodes.length && other_nodes.length) {
41778             nodes= other_nodes;
41779         }
41780         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41781             return false;
41782         }
41783         
41784         return nodes[0];
41785     },
41786     createRange: function(sel)
41787     {
41788         // this has strange effects when using with 
41789         // top toolbar - not sure if it's a great idea.
41790         //this.editor.contentWindow.focus();
41791         if (typeof sel != "undefined") {
41792             try {
41793                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41794             } catch(e) {
41795                 return this.doc.createRange();
41796             }
41797         } else {
41798             return this.doc.createRange();
41799         }
41800     },
41801     getParentElement: function()
41802     {
41803         
41804         this.assignDocWin();
41805         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41806         
41807         var range = this.createRange(sel);
41808          
41809         try {
41810             var p = range.commonAncestorContainer;
41811             while (p.nodeType == 3) { // text node
41812                 p = p.parentNode;
41813             }
41814             return p;
41815         } catch (e) {
41816             return null;
41817         }
41818     
41819     },
41820     /***
41821      *
41822      * Range intersection.. the hard stuff...
41823      *  '-1' = before
41824      *  '0' = hits..
41825      *  '1' = after.
41826      *         [ -- selected range --- ]
41827      *   [fail]                        [fail]
41828      *
41829      *    basically..
41830      *      if end is before start or  hits it. fail.
41831      *      if start is after end or hits it fail.
41832      *
41833      *   if either hits (but other is outside. - then it's not 
41834      *   
41835      *    
41836      **/
41837     
41838     
41839     // @see http://www.thismuchiknow.co.uk/?p=64.
41840     rangeIntersectsNode : function(range, node)
41841     {
41842         var nodeRange = node.ownerDocument.createRange();
41843         try {
41844             nodeRange.selectNode(node);
41845         } catch (e) {
41846             nodeRange.selectNodeContents(node);
41847         }
41848     
41849         var rangeStartRange = range.cloneRange();
41850         rangeStartRange.collapse(true);
41851     
41852         var rangeEndRange = range.cloneRange();
41853         rangeEndRange.collapse(false);
41854     
41855         var nodeStartRange = nodeRange.cloneRange();
41856         nodeStartRange.collapse(true);
41857     
41858         var nodeEndRange = nodeRange.cloneRange();
41859         nodeEndRange.collapse(false);
41860     
41861         return rangeStartRange.compareBoundaryPoints(
41862                  Range.START_TO_START, nodeEndRange) == -1 &&
41863                rangeEndRange.compareBoundaryPoints(
41864                  Range.START_TO_START, nodeStartRange) == 1;
41865         
41866          
41867     },
41868     rangeCompareNode : function(range, node)
41869     {
41870         var nodeRange = node.ownerDocument.createRange();
41871         try {
41872             nodeRange.selectNode(node);
41873         } catch (e) {
41874             nodeRange.selectNodeContents(node);
41875         }
41876         
41877         
41878         range.collapse(true);
41879     
41880         nodeRange.collapse(true);
41881      
41882         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41883         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41884          
41885         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41886         
41887         var nodeIsBefore   =  ss == 1;
41888         var nodeIsAfter    = ee == -1;
41889         
41890         if (nodeIsBefore && nodeIsAfter)
41891             return 0; // outer
41892         if (!nodeIsBefore && nodeIsAfter)
41893             return 1; //right trailed.
41894         
41895         if (nodeIsBefore && !nodeIsAfter)
41896             return 2;  // left trailed.
41897         // fully contined.
41898         return 3;
41899     },
41900
41901     // private? - in a new class?
41902     cleanUpPaste :  function()
41903     {
41904         // cleans up the whole document..
41905          Roo.log('cleanuppaste');
41906         this.cleanUpChildren(this.doc.body);
41907         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41908         if (clean != this.doc.body.innerHTML) {
41909             this.doc.body.innerHTML = clean;
41910         }
41911         
41912     },
41913     
41914     cleanWordChars : function(input) {// change the chars to hex code
41915         var he = Roo.form.HtmlEditor;
41916         
41917         var output = input;
41918         Roo.each(he.swapCodes, function(sw) { 
41919             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41920             
41921             output = output.replace(swapper, sw[1]);
41922         });
41923         
41924         return output;
41925     },
41926     
41927     
41928     cleanUpChildren : function (n)
41929     {
41930         if (!n.childNodes.length) {
41931             return;
41932         }
41933         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41934            this.cleanUpChild(n.childNodes[i]);
41935         }
41936     },
41937     
41938     
41939         
41940     
41941     cleanUpChild : function (node)
41942     {
41943         var ed = this;
41944         //console.log(node);
41945         if (node.nodeName == "#text") {
41946             // clean up silly Windows -- stuff?
41947             return; 
41948         }
41949         if (node.nodeName == "#comment") {
41950             node.parentNode.removeChild(node);
41951             // clean up silly Windows -- stuff?
41952             return; 
41953         }
41954         
41955         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41956             // remove node.
41957             node.parentNode.removeChild(node);
41958             return;
41959             
41960         }
41961         
41962         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41963         
41964         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41965         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41966         
41967         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41968         //    remove_keep_children = true;
41969         //}
41970         
41971         if (remove_keep_children) {
41972             this.cleanUpChildren(node);
41973             // inserts everything just before this node...
41974             while (node.childNodes.length) {
41975                 var cn = node.childNodes[0];
41976                 node.removeChild(cn);
41977                 node.parentNode.insertBefore(cn, node);
41978             }
41979             node.parentNode.removeChild(node);
41980             return;
41981         }
41982         
41983         if (!node.attributes || !node.attributes.length) {
41984             this.cleanUpChildren(node);
41985             return;
41986         }
41987         
41988         function cleanAttr(n,v)
41989         {
41990             
41991             if (v.match(/^\./) || v.match(/^\//)) {
41992                 return;
41993             }
41994             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41995                 return;
41996             }
41997             if (v.match(/^#/)) {
41998                 return;
41999             }
42000 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42001             node.removeAttribute(n);
42002             
42003         }
42004         
42005         function cleanStyle(n,v)
42006         {
42007             if (v.match(/expression/)) { //XSS?? should we even bother..
42008                 node.removeAttribute(n);
42009                 return;
42010             }
42011             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
42012             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
42013             
42014             
42015             var parts = v.split(/;/);
42016             var clean = [];
42017             
42018             Roo.each(parts, function(p) {
42019                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42020                 if (!p.length) {
42021                     return true;
42022                 }
42023                 var l = p.split(':').shift().replace(/\s+/g,'');
42024                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42025                 
42026                 
42027                 if ( cblack.indexOf(l) > -1) {
42028 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42029                     //node.removeAttribute(n);
42030                     return true;
42031                 }
42032                 //Roo.log()
42033                 // only allow 'c whitelisted system attributes'
42034                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42035 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42036                     //node.removeAttribute(n);
42037                     return true;
42038                 }
42039                 
42040                 
42041                  
42042                 
42043                 clean.push(p);
42044                 return true;
42045             });
42046             if (clean.length) { 
42047                 node.setAttribute(n, clean.join(';'));
42048             } else {
42049                 node.removeAttribute(n);
42050             }
42051             
42052         }
42053         
42054         
42055         for (var i = node.attributes.length-1; i > -1 ; i--) {
42056             var a = node.attributes[i];
42057             //console.log(a);
42058             
42059             if (a.name.toLowerCase().substr(0,2)=='on')  {
42060                 node.removeAttribute(a.name);
42061                 continue;
42062             }
42063             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
42064                 node.removeAttribute(a.name);
42065                 continue;
42066             }
42067             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
42068                 cleanAttr(a.name,a.value); // fixme..
42069                 continue;
42070             }
42071             if (a.name == 'style') {
42072                 cleanStyle(a.name,a.value);
42073                 continue;
42074             }
42075             /// clean up MS crap..
42076             // tecnically this should be a list of valid class'es..
42077             
42078             
42079             if (a.name == 'class') {
42080                 if (a.value.match(/^Mso/)) {
42081                     node.className = '';
42082                 }
42083                 
42084                 if (a.value.match(/body/)) {
42085                     node.className = '';
42086                 }
42087                 continue;
42088             }
42089             
42090             // style cleanup!?
42091             // class cleanup?
42092             
42093         }
42094         
42095         
42096         this.cleanUpChildren(node);
42097         
42098         
42099     }
42100     
42101     
42102     // hide stuff that is not compatible
42103     /**
42104      * @event blur
42105      * @hide
42106      */
42107     /**
42108      * @event change
42109      * @hide
42110      */
42111     /**
42112      * @event focus
42113      * @hide
42114      */
42115     /**
42116      * @event specialkey
42117      * @hide
42118      */
42119     /**
42120      * @cfg {String} fieldClass @hide
42121      */
42122     /**
42123      * @cfg {String} focusClass @hide
42124      */
42125     /**
42126      * @cfg {String} autoCreate @hide
42127      */
42128     /**
42129      * @cfg {String} inputType @hide
42130      */
42131     /**
42132      * @cfg {String} invalidClass @hide
42133      */
42134     /**
42135      * @cfg {String} invalidText @hide
42136      */
42137     /**
42138      * @cfg {String} msgFx @hide
42139      */
42140     /**
42141      * @cfg {String} validateOnBlur @hide
42142      */
42143 });
42144
42145 Roo.form.HtmlEditor.white = [
42146         'area', 'br', 'img', 'input', 'hr', 'wbr',
42147         
42148        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42149        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42150        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42151        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42152        'table',   'ul',         'xmp', 
42153        
42154        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42155       'thead',   'tr', 
42156      
42157       'dir', 'menu', 'ol', 'ul', 'dl',
42158        
42159       'embed',  'object'
42160 ];
42161
42162
42163 Roo.form.HtmlEditor.black = [
42164     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42165         'applet', // 
42166         'base',   'basefont', 'bgsound', 'blink',  'body', 
42167         'frame',  'frameset', 'head',    'html',   'ilayer', 
42168         'iframe', 'layer',  'link',     'meta',    'object',   
42169         'script', 'style' ,'title',  'xml' // clean later..
42170 ];
42171 Roo.form.HtmlEditor.clean = [
42172     'script', 'style', 'title', 'xml'
42173 ];
42174 Roo.form.HtmlEditor.remove = [
42175     'font'
42176 ];
42177 // attributes..
42178
42179 Roo.form.HtmlEditor.ablack = [
42180     'on'
42181 ];
42182     
42183 Roo.form.HtmlEditor.aclean = [ 
42184     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42185 ];
42186
42187 // protocols..
42188 Roo.form.HtmlEditor.pwhite= [
42189         'http',  'https',  'mailto'
42190 ];
42191
42192 // white listed style attributes.
42193 Roo.form.HtmlEditor.cwhite= [
42194       //  'text-align', /// default is to allow most things..
42195       
42196          
42197 //        'font-size'//??
42198 ];
42199
42200 // black listed style attributes.
42201 Roo.form.HtmlEditor.cblack= [
42202       //  'font-size' -- this can be set by the project 
42203 ];
42204
42205
42206 Roo.form.HtmlEditor.swapCodes   =[ 
42207     [    8211, "--" ], 
42208     [    8212, "--" ], 
42209     [    8216,  "'" ],  
42210     [    8217, "'" ],  
42211     [    8220, '"' ],  
42212     [    8221, '"' ],  
42213     [    8226, "*" ],  
42214     [    8230, "..." ]
42215 ]; 
42216
42217     // <script type="text/javascript">
42218 /*
42219  * Based on
42220  * Ext JS Library 1.1.1
42221  * Copyright(c) 2006-2007, Ext JS, LLC.
42222  *  
42223  
42224  */
42225
42226 /**
42227  * @class Roo.form.HtmlEditorToolbar1
42228  * Basic Toolbar
42229  * 
42230  * Usage:
42231  *
42232  new Roo.form.HtmlEditor({
42233     ....
42234     toolbars : [
42235         new Roo.form.HtmlEditorToolbar1({
42236             disable : { fonts: 1 , format: 1, ..., ... , ...],
42237             btns : [ .... ]
42238         })
42239     }
42240      
42241  * 
42242  * @cfg {Object} disable List of elements to disable..
42243  * @cfg {Array} btns List of additional buttons.
42244  * 
42245  * 
42246  * NEEDS Extra CSS? 
42247  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42248  */
42249  
42250 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42251 {
42252     
42253     Roo.apply(this, config);
42254     
42255     // default disabled, based on 'good practice'..
42256     this.disable = this.disable || {};
42257     Roo.applyIf(this.disable, {
42258         fontSize : true,
42259         colors : true,
42260         specialElements : true
42261     });
42262     
42263     
42264     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42265     // dont call parent... till later.
42266 }
42267
42268 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42269     
42270     tb: false,
42271     
42272     rendered: false,
42273     
42274     editor : false,
42275     /**
42276      * @cfg {Object} disable  List of toolbar elements to disable
42277          
42278      */
42279     disable : false,
42280       /**
42281      * @cfg {Array} fontFamilies An array of available font families
42282      */
42283     fontFamilies : [
42284         'Arial',
42285         'Courier New',
42286         'Tahoma',
42287         'Times New Roman',
42288         'Verdana'
42289     ],
42290     
42291     specialChars : [
42292            "&#169;",
42293           "&#174;",     
42294           "&#8482;",    
42295           "&#163;" ,    
42296          // "&#8212;",    
42297           "&#8230;",    
42298           "&#247;" ,    
42299         //  "&#225;" ,     ?? a acute?
42300            "&#8364;"    , //Euro
42301        //   "&#8220;"    ,
42302         //  "&#8221;"    ,
42303         //  "&#8226;"    ,
42304           "&#176;"  //   , // degrees
42305
42306          // "&#233;"     , // e ecute
42307          // "&#250;"     , // u ecute?
42308     ],
42309     
42310     specialElements : [
42311         {
42312             text: "Insert Table",
42313             xtype: 'MenuItem',
42314             xns : Roo.Menu,
42315             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42316                 
42317         },
42318         {    
42319             text: "Insert Image",
42320             xtype: 'MenuItem',
42321             xns : Roo.Menu,
42322             ihtml : '<img src="about:blank"/>'
42323             
42324         }
42325         
42326          
42327     ],
42328     
42329     
42330     inputElements : [ 
42331             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42332             "input:submit", "input:button", "select", "textarea", "label" ],
42333     formats : [
42334         ["p"] ,  
42335         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42336         ["pre"],[ "code"], 
42337         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42338         ['div'],['span']
42339     ],
42340     
42341     cleanStyles : [
42342         "font-size"
42343     ],
42344      /**
42345      * @cfg {String} defaultFont default font to use.
42346      */
42347     defaultFont: 'tahoma',
42348    
42349     fontSelect : false,
42350     
42351     
42352     formatCombo : false,
42353     
42354     init : function(editor)
42355     {
42356         this.editor = editor;
42357         
42358         
42359         var fid = editor.frameId;
42360         var etb = this;
42361         function btn(id, toggle, handler){
42362             var xid = fid + '-'+ id ;
42363             return {
42364                 id : xid,
42365                 cmd : id,
42366                 cls : 'x-btn-icon x-edit-'+id,
42367                 enableToggle:toggle !== false,
42368                 scope: editor, // was editor...
42369                 handler:handler||editor.relayBtnCmd,
42370                 clickEvent:'mousedown',
42371                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42372                 tabIndex:-1
42373             };
42374         }
42375         
42376         
42377         
42378         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42379         this.tb = tb;
42380          // stop form submits
42381         tb.el.on('click', function(e){
42382             e.preventDefault(); // what does this do?
42383         });
42384
42385         if(!this.disable.font) { // && !Roo.isSafari){
42386             /* why no safari for fonts 
42387             editor.fontSelect = tb.el.createChild({
42388                 tag:'select',
42389                 tabIndex: -1,
42390                 cls:'x-font-select',
42391                 html: this.createFontOptions()
42392             });
42393             
42394             editor.fontSelect.on('change', function(){
42395                 var font = editor.fontSelect.dom.value;
42396                 editor.relayCmd('fontname', font);
42397                 editor.deferFocus();
42398             }, editor);
42399             
42400             tb.add(
42401                 editor.fontSelect.dom,
42402                 '-'
42403             );
42404             */
42405             
42406         };
42407         if(!this.disable.formats){
42408             this.formatCombo = new Roo.form.ComboBox({
42409                 store: new Roo.data.SimpleStore({
42410                     id : 'tag',
42411                     fields: ['tag'],
42412                     data : this.formats // from states.js
42413                 }),
42414                 blockFocus : true,
42415                 name : '',
42416                 //autoCreate : {tag: "div",  size: "20"},
42417                 displayField:'tag',
42418                 typeAhead: false,
42419                 mode: 'local',
42420                 editable : false,
42421                 triggerAction: 'all',
42422                 emptyText:'Add tag',
42423                 selectOnFocus:true,
42424                 width:135,
42425                 listeners : {
42426                     'select': function(c, r, i) {
42427                         editor.insertTag(r.get('tag'));
42428                         editor.focus();
42429                     }
42430                 }
42431
42432             });
42433             tb.addField(this.formatCombo);
42434             
42435         }
42436         
42437         if(!this.disable.format){
42438             tb.add(
42439                 btn('bold'),
42440                 btn('italic'),
42441                 btn('underline')
42442             );
42443         };
42444         if(!this.disable.fontSize){
42445             tb.add(
42446                 '-',
42447                 
42448                 
42449                 btn('increasefontsize', false, editor.adjustFont),
42450                 btn('decreasefontsize', false, editor.adjustFont)
42451             );
42452         };
42453         
42454         
42455         if(!this.disable.colors){
42456             tb.add(
42457                 '-', {
42458                     id:editor.frameId +'-forecolor',
42459                     cls:'x-btn-icon x-edit-forecolor',
42460                     clickEvent:'mousedown',
42461                     tooltip: this.buttonTips['forecolor'] || undefined,
42462                     tabIndex:-1,
42463                     menu : new Roo.menu.ColorMenu({
42464                         allowReselect: true,
42465                         focus: Roo.emptyFn,
42466                         value:'000000',
42467                         plain:true,
42468                         selectHandler: function(cp, color){
42469                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42470                             editor.deferFocus();
42471                         },
42472                         scope: editor,
42473                         clickEvent:'mousedown'
42474                     })
42475                 }, {
42476                     id:editor.frameId +'backcolor',
42477                     cls:'x-btn-icon x-edit-backcolor',
42478                     clickEvent:'mousedown',
42479                     tooltip: this.buttonTips['backcolor'] || undefined,
42480                     tabIndex:-1,
42481                     menu : new Roo.menu.ColorMenu({
42482                         focus: Roo.emptyFn,
42483                         value:'FFFFFF',
42484                         plain:true,
42485                         allowReselect: true,
42486                         selectHandler: function(cp, color){
42487                             if(Roo.isGecko){
42488                                 editor.execCmd('useCSS', false);
42489                                 editor.execCmd('hilitecolor', color);
42490                                 editor.execCmd('useCSS', true);
42491                                 editor.deferFocus();
42492                             }else{
42493                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42494                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42495                                 editor.deferFocus();
42496                             }
42497                         },
42498                         scope:editor,
42499                         clickEvent:'mousedown'
42500                     })
42501                 }
42502             );
42503         };
42504         // now add all the items...
42505         
42506
42507         if(!this.disable.alignments){
42508             tb.add(
42509                 '-',
42510                 btn('justifyleft'),
42511                 btn('justifycenter'),
42512                 btn('justifyright')
42513             );
42514         };
42515
42516         //if(!Roo.isSafari){
42517             if(!this.disable.links){
42518                 tb.add(
42519                     '-',
42520                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
42521                 );
42522             };
42523
42524             if(!this.disable.lists){
42525                 tb.add(
42526                     '-',
42527                     btn('insertorderedlist'),
42528                     btn('insertunorderedlist')
42529                 );
42530             }
42531             if(!this.disable.sourceEdit){
42532                 tb.add(
42533                     '-',
42534                     btn('sourceedit', true, function(btn){
42535                         this.toggleSourceEdit(btn.pressed);
42536                     })
42537                 );
42538             }
42539         //}
42540         
42541         var smenu = { };
42542         // special menu.. - needs to be tidied up..
42543         if (!this.disable.special) {
42544             smenu = {
42545                 text: "&#169;",
42546                 cls: 'x-edit-none',
42547                 
42548                 menu : {
42549                     items : []
42550                 }
42551             };
42552             for (var i =0; i < this.specialChars.length; i++) {
42553                 smenu.menu.items.push({
42554                     
42555                     html: this.specialChars[i],
42556                     handler: function(a,b) {
42557                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42558                         //editor.insertAtCursor(a.html);
42559                         
42560                     },
42561                     tabIndex:-1
42562                 });
42563             }
42564             
42565             
42566             tb.add(smenu);
42567             
42568             
42569         }
42570         
42571         var cmenu = { };
42572         if (!this.disable.cleanStyles) {
42573             cmenu = {
42574                 cls: 'x-btn-icon x-btn-clear',
42575                 
42576                 menu : {
42577                     items : []
42578                 }
42579             };
42580             for (var i =0; i < this.cleanStyles.length; i++) {
42581                 cmenu.menu.items.push({
42582                     actiontype : this.cleanStyles[i],
42583                     html: 'Remove ' + this.cleanStyles[i],
42584                     handler: function(a,b) {
42585                         Roo.log(a);
42586                         Roo.log(b);
42587                         var c = Roo.get(editor.doc.body);
42588                         c.select('[style]').each(function(s) {
42589                             s.dom.style.removeProperty(a.actiontype);
42590                         });
42591                         
42592                     },
42593                     tabIndex:-1
42594                 });
42595             }
42596             
42597             tb.add(cmenu);
42598         }
42599          
42600         if (!this.disable.specialElements) {
42601             var semenu = {
42602                 text: "Other;",
42603                 cls: 'x-edit-none',
42604                 menu : {
42605                     items : []
42606                 }
42607             };
42608             for (var i =0; i < this.specialElements.length; i++) {
42609                 semenu.menu.items.push(
42610                     Roo.apply({ 
42611                         handler: function(a,b) {
42612                             editor.insertAtCursor(this.ihtml);
42613                         }
42614                     }, this.specialElements[i])
42615                 );
42616                     
42617             }
42618             
42619             tb.add(semenu);
42620             
42621             
42622         }
42623          
42624         
42625         if (this.btns) {
42626             for(var i =0; i< this.btns.length;i++) {
42627                 var b = Roo.factory(this.btns[i],Roo.form);
42628                 b.cls =  'x-edit-none';
42629                 b.scope = editor;
42630                 tb.add(b);
42631             }
42632         
42633         }
42634         
42635         
42636         
42637         // disable everything...
42638         
42639         this.tb.items.each(function(item){
42640            if(item.id != editor.frameId+ '-sourceedit'){
42641                 item.disable();
42642             }
42643         });
42644         this.rendered = true;
42645         
42646         // the all the btns;
42647         editor.on('editorevent', this.updateToolbar, this);
42648         // other toolbars need to implement this..
42649         //editor.on('editmodechange', this.updateToolbar, this);
42650     },
42651     
42652     
42653     
42654     /**
42655      * Protected method that will not generally be called directly. It triggers
42656      * a toolbar update by reading the markup state of the current selection in the editor.
42657      */
42658     updateToolbar: function(){
42659
42660         if(!this.editor.activated){
42661             this.editor.onFirstFocus();
42662             return;
42663         }
42664
42665         var btns = this.tb.items.map, 
42666             doc = this.editor.doc,
42667             frameId = this.editor.frameId;
42668
42669         if(!this.disable.font && !Roo.isSafari){
42670             /*
42671             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
42672             if(name != this.fontSelect.dom.value){
42673                 this.fontSelect.dom.value = name;
42674             }
42675             */
42676         }
42677         if(!this.disable.format){
42678             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
42679             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
42680             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
42681         }
42682         if(!this.disable.alignments){
42683             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
42684             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
42685             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
42686         }
42687         if(!Roo.isSafari && !this.disable.lists){
42688             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
42689             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
42690         }
42691         
42692         var ans = this.editor.getAllAncestors();
42693         if (this.formatCombo) {
42694             
42695             
42696             var store = this.formatCombo.store;
42697             this.formatCombo.setValue("");
42698             for (var i =0; i < ans.length;i++) {
42699                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
42700                     // select it..
42701                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42702                     break;
42703                 }
42704             }
42705         }
42706         
42707         
42708         
42709         // hides menus... - so this cant be on a menu...
42710         Roo.menu.MenuMgr.hideAll();
42711
42712         //this.editorsyncValue();
42713     },
42714    
42715     
42716     createFontOptions : function(){
42717         var buf = [], fs = this.fontFamilies, ff, lc;
42718         
42719         
42720         
42721         for(var i = 0, len = fs.length; i< len; i++){
42722             ff = fs[i];
42723             lc = ff.toLowerCase();
42724             buf.push(
42725                 '<option value="',lc,'" style="font-family:',ff,';"',
42726                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42727                     ff,
42728                 '</option>'
42729             );
42730         }
42731         return buf.join('');
42732     },
42733     
42734     toggleSourceEdit : function(sourceEditMode){
42735         if(sourceEditMode === undefined){
42736             sourceEditMode = !this.sourceEditMode;
42737         }
42738         this.sourceEditMode = sourceEditMode === true;
42739         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42740         // just toggle the button?
42741         if(btn.pressed !== this.editor.sourceEditMode){
42742             btn.toggle(this.editor.sourceEditMode);
42743             return;
42744         }
42745         
42746         if(this.sourceEditMode){
42747             this.tb.items.each(function(item){
42748                 if(item.cmd != 'sourceedit'){
42749                     item.disable();
42750                 }
42751             });
42752           
42753         }else{
42754             if(this.initialized){
42755                 this.tb.items.each(function(item){
42756                     item.enable();
42757                 });
42758             }
42759             
42760         }
42761         // tell the editor that it's been pressed..
42762         this.editor.toggleSourceEdit(sourceEditMode);
42763        
42764     },
42765      /**
42766      * Object collection of toolbar tooltips for the buttons in the editor. The key
42767      * is the command id associated with that button and the value is a valid QuickTips object.
42768      * For example:
42769 <pre><code>
42770 {
42771     bold : {
42772         title: 'Bold (Ctrl+B)',
42773         text: 'Make the selected text bold.',
42774         cls: 'x-html-editor-tip'
42775     },
42776     italic : {
42777         title: 'Italic (Ctrl+I)',
42778         text: 'Make the selected text italic.',
42779         cls: 'x-html-editor-tip'
42780     },
42781     ...
42782 </code></pre>
42783     * @type Object
42784      */
42785     buttonTips : {
42786         bold : {
42787             title: 'Bold (Ctrl+B)',
42788             text: 'Make the selected text bold.',
42789             cls: 'x-html-editor-tip'
42790         },
42791         italic : {
42792             title: 'Italic (Ctrl+I)',
42793             text: 'Make the selected text italic.',
42794             cls: 'x-html-editor-tip'
42795         },
42796         underline : {
42797             title: 'Underline (Ctrl+U)',
42798             text: 'Underline the selected text.',
42799             cls: 'x-html-editor-tip'
42800         },
42801         increasefontsize : {
42802             title: 'Grow Text',
42803             text: 'Increase the font size.',
42804             cls: 'x-html-editor-tip'
42805         },
42806         decreasefontsize : {
42807             title: 'Shrink Text',
42808             text: 'Decrease the font size.',
42809             cls: 'x-html-editor-tip'
42810         },
42811         backcolor : {
42812             title: 'Text Highlight Color',
42813             text: 'Change the background color of the selected text.',
42814             cls: 'x-html-editor-tip'
42815         },
42816         forecolor : {
42817             title: 'Font Color',
42818             text: 'Change the color of the selected text.',
42819             cls: 'x-html-editor-tip'
42820         },
42821         justifyleft : {
42822             title: 'Align Text Left',
42823             text: 'Align text to the left.',
42824             cls: 'x-html-editor-tip'
42825         },
42826         justifycenter : {
42827             title: 'Center Text',
42828             text: 'Center text in the editor.',
42829             cls: 'x-html-editor-tip'
42830         },
42831         justifyright : {
42832             title: 'Align Text Right',
42833             text: 'Align text to the right.',
42834             cls: 'x-html-editor-tip'
42835         },
42836         insertunorderedlist : {
42837             title: 'Bullet List',
42838             text: 'Start a bulleted list.',
42839             cls: 'x-html-editor-tip'
42840         },
42841         insertorderedlist : {
42842             title: 'Numbered List',
42843             text: 'Start a numbered list.',
42844             cls: 'x-html-editor-tip'
42845         },
42846         createlink : {
42847             title: 'Hyperlink',
42848             text: 'Make the selected text a hyperlink.',
42849             cls: 'x-html-editor-tip'
42850         },
42851         sourceedit : {
42852             title: 'Source Edit',
42853             text: 'Switch to source editing mode.',
42854             cls: 'x-html-editor-tip'
42855         }
42856     },
42857     // private
42858     onDestroy : function(){
42859         if(this.rendered){
42860             
42861             this.tb.items.each(function(item){
42862                 if(item.menu){
42863                     item.menu.removeAll();
42864                     if(item.menu.el){
42865                         item.menu.el.destroy();
42866                     }
42867                 }
42868                 item.destroy();
42869             });
42870              
42871         }
42872     },
42873     onFirstFocus: function() {
42874         this.tb.items.each(function(item){
42875            item.enable();
42876         });
42877     }
42878 });
42879
42880
42881
42882
42883 // <script type="text/javascript">
42884 /*
42885  * Based on
42886  * Ext JS Library 1.1.1
42887  * Copyright(c) 2006-2007, Ext JS, LLC.
42888  *  
42889  
42890  */
42891
42892  
42893 /**
42894  * @class Roo.form.HtmlEditor.ToolbarContext
42895  * Context Toolbar
42896  * 
42897  * Usage:
42898  *
42899  new Roo.form.HtmlEditor({
42900     ....
42901     toolbars : [
42902         { xtype: 'ToolbarStandard', styles : {} }
42903         { xtype: 'ToolbarContext', disable : {} }
42904     ]
42905 })
42906
42907      
42908  * 
42909  * @config : {Object} disable List of elements to disable.. (not done yet.)
42910  * @config : {Object} styles  Map of styles available.
42911  * 
42912  */
42913
42914 Roo.form.HtmlEditor.ToolbarContext = function(config)
42915 {
42916     
42917     Roo.apply(this, config);
42918     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42919     // dont call parent... till later.
42920     this.styles = this.styles || {};
42921 }
42922
42923  
42924
42925 Roo.form.HtmlEditor.ToolbarContext.types = {
42926     'IMG' : {
42927         width : {
42928             title: "Width",
42929             width: 40
42930         },
42931         height:  {
42932             title: "Height",
42933             width: 40
42934         },
42935         align: {
42936             title: "Align",
42937             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42938             width : 80
42939             
42940         },
42941         border: {
42942             title: "Border",
42943             width: 40
42944         },
42945         alt: {
42946             title: "Alt",
42947             width: 120
42948         },
42949         src : {
42950             title: "Src",
42951             width: 220
42952         }
42953         
42954     },
42955     'A' : {
42956         name : {
42957             title: "Name",
42958             width: 50
42959         },
42960         target:  {
42961             title: "Target",
42962             width: 120
42963         },
42964         href:  {
42965             title: "Href",
42966             width: 220
42967         } // border?
42968         
42969     },
42970     'TABLE' : {
42971         rows : {
42972             title: "Rows",
42973             width: 20
42974         },
42975         cols : {
42976             title: "Cols",
42977             width: 20
42978         },
42979         width : {
42980             title: "Width",
42981             width: 40
42982         },
42983         height : {
42984             title: "Height",
42985             width: 40
42986         },
42987         border : {
42988             title: "Border",
42989             width: 20
42990         }
42991     },
42992     'TD' : {
42993         width : {
42994             title: "Width",
42995             width: 40
42996         },
42997         height : {
42998             title: "Height",
42999             width: 40
43000         },   
43001         align: {
43002             title: "Align",
43003             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43004             width: 80
43005         },
43006         valign: {
43007             title: "Valign",
43008             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43009             width: 80
43010         },
43011         colspan: {
43012             title: "Colspan",
43013             width: 20
43014             
43015         },
43016          'font-family'  : {
43017             title : "Font",
43018             style : 'fontFamily',
43019             displayField: 'display',
43020             optname : 'font-family',
43021             width: 140
43022         }
43023     },
43024     'INPUT' : {
43025         name : {
43026             title: "name",
43027             width: 120
43028         },
43029         value : {
43030             title: "Value",
43031             width: 120
43032         },
43033         width : {
43034             title: "Width",
43035             width: 40
43036         }
43037     },
43038     'LABEL' : {
43039         'for' : {
43040             title: "For",
43041             width: 120
43042         }
43043     },
43044     'TEXTAREA' : {
43045           name : {
43046             title: "name",
43047             width: 120
43048         },
43049         rows : {
43050             title: "Rows",
43051             width: 20
43052         },
43053         cols : {
43054             title: "Cols",
43055             width: 20
43056         }
43057     },
43058     'SELECT' : {
43059         name : {
43060             title: "name",
43061             width: 120
43062         },
43063         selectoptions : {
43064             title: "Options",
43065             width: 200
43066         }
43067     },
43068     
43069     // should we really allow this??
43070     // should this just be 
43071     'BODY' : {
43072         title : {
43073             title: "Title",
43074             width: 200,
43075             disabled : true
43076         }
43077     },
43078     'SPAN' : {
43079         'font-family'  : {
43080             title : "Font",
43081             style : 'fontFamily',
43082             displayField: 'display',
43083             optname : 'font-family',
43084             width: 140
43085         }
43086     },
43087     'DIV' : {
43088         'font-family'  : {
43089             title : "Font",
43090             style : 'fontFamily',
43091             displayField: 'display',
43092             optname : 'font-family',
43093             width: 140
43094         }
43095     },
43096      'P' : {
43097         'font-family'  : {
43098             title : "Font",
43099             style : 'fontFamily',
43100             displayField: 'display',
43101             optname : 'font-family',
43102             width: 140
43103         }
43104     },
43105     
43106     '*' : {
43107         // empty..
43108     }
43109
43110 };
43111
43112 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43113 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43114
43115 Roo.form.HtmlEditor.ToolbarContext.options = {
43116         'font-family'  : [ 
43117                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43118                 [ 'Courier New', 'Courier New'],
43119                 [ 'Tahoma', 'Tahoma'],
43120                 [ 'Times New Roman,serif', 'Times'],
43121                 [ 'Verdana','Verdana' ]
43122         ]
43123 };
43124
43125 // fixme - these need to be configurable..
43126  
43127
43128 Roo.form.HtmlEditor.ToolbarContext.types
43129
43130
43131 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43132     
43133     tb: false,
43134     
43135     rendered: false,
43136     
43137     editor : false,
43138     /**
43139      * @cfg {Object} disable  List of toolbar elements to disable
43140          
43141      */
43142     disable : false,
43143     /**
43144      * @cfg {Object} styles List of styles 
43145      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43146      *
43147      * These must be defined in the page, so they get rendered correctly..
43148      * .headline { }
43149      * TD.underline { }
43150      * 
43151      */
43152     styles : false,
43153     
43154     options: false,
43155     
43156     toolbars : false,
43157     
43158     init : function(editor)
43159     {
43160         this.editor = editor;
43161         
43162         
43163         var fid = editor.frameId;
43164         var etb = this;
43165         function btn(id, toggle, handler){
43166             var xid = fid + '-'+ id ;
43167             return {
43168                 id : xid,
43169                 cmd : id,
43170                 cls : 'x-btn-icon x-edit-'+id,
43171                 enableToggle:toggle !== false,
43172                 scope: editor, // was editor...
43173                 handler:handler||editor.relayBtnCmd,
43174                 clickEvent:'mousedown',
43175                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43176                 tabIndex:-1
43177             };
43178         }
43179         // create a new element.
43180         var wdiv = editor.wrap.createChild({
43181                 tag: 'div'
43182             }, editor.wrap.dom.firstChild.nextSibling, true);
43183         
43184         // can we do this more than once??
43185         
43186          // stop form submits
43187       
43188  
43189         // disable everything...
43190         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43191         this.toolbars = {};
43192            
43193         for (var i in  ty) {
43194           
43195             this.toolbars[i] = this.buildToolbar(ty[i],i);
43196         }
43197         this.tb = this.toolbars.BODY;
43198         this.tb.el.show();
43199         this.buildFooter();
43200         this.footer.show();
43201         editor.on('hide', function( ) { this.footer.hide() }, this);
43202         editor.on('show', function( ) { this.footer.show() }, this);
43203         
43204          
43205         this.rendered = true;
43206         
43207         // the all the btns;
43208         editor.on('editorevent', this.updateToolbar, this);
43209         // other toolbars need to implement this..
43210         //editor.on('editmodechange', this.updateToolbar, this);
43211     },
43212     
43213     
43214     
43215     /**
43216      * Protected method that will not generally be called directly. It triggers
43217      * a toolbar update by reading the markup state of the current selection in the editor.
43218      */
43219     updateToolbar: function(editor,ev,sel){
43220
43221         //Roo.log(ev);
43222         // capture mouse up - this is handy for selecting images..
43223         // perhaps should go somewhere else...
43224         if(!this.editor.activated){
43225              this.editor.onFirstFocus();
43226             return;
43227         }
43228         
43229         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43230         // selectNode - might want to handle IE?
43231         if (ev &&
43232             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43233             ev.target && ev.target.tagName == 'IMG') {
43234             // they have click on an image...
43235             // let's see if we can change the selection...
43236             sel = ev.target;
43237          
43238               var nodeRange = sel.ownerDocument.createRange();
43239             try {
43240                 nodeRange.selectNode(sel);
43241             } catch (e) {
43242                 nodeRange.selectNodeContents(sel);
43243             }
43244             //nodeRange.collapse(true);
43245             var s = editor.win.getSelection();
43246             s.removeAllRanges();
43247             s.addRange(nodeRange);
43248         }  
43249         
43250       
43251         var updateFooter = sel ? false : true;
43252         
43253         
43254         var ans = this.editor.getAllAncestors();
43255         
43256         // pick
43257         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43258         
43259         if (!sel) { 
43260             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
43261             sel = sel ? sel : this.editor.doc.body;
43262             sel = sel.tagName.length ? sel : this.editor.doc.body;
43263             
43264         }
43265         // pick a menu that exists..
43266         var tn = sel.tagName.toUpperCase();
43267         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43268         
43269         tn = sel.tagName.toUpperCase();
43270         
43271         var lastSel = this.tb.selectedNode
43272         
43273         this.tb.selectedNode = sel;
43274         
43275         // if current menu does not match..
43276         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43277                 
43278             this.tb.el.hide();
43279             ///console.log("show: " + tn);
43280             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43281             this.tb.el.show();
43282             // update name
43283             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43284             
43285             
43286             // update attributes
43287             if (this.tb.fields) {
43288                 this.tb.fields.each(function(e) {
43289                     if (e.stylename) {
43290                         e.setValue(sel.style[e.stylename]);
43291                         return;
43292                     } 
43293                    e.setValue(sel.getAttribute(e.attrname));
43294                 });
43295             }
43296             
43297             var hasStyles = false;
43298             for(var i in this.styles) {
43299                 hasStyles = true;
43300                 break;
43301             }
43302             
43303             // update styles
43304             if (hasStyles) { 
43305                 var st = this.tb.fields.item(0);
43306                 
43307                 st.store.removeAll();
43308                
43309                 
43310                 var cn = sel.className.split(/\s+/);
43311                 
43312                 var avs = [];
43313                 if (this.styles['*']) {
43314                     
43315                     Roo.each(this.styles['*'], function(v) {
43316                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43317                     });
43318                 }
43319                 if (this.styles[tn]) { 
43320                     Roo.each(this.styles[tn], function(v) {
43321                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43322                     });
43323                 }
43324                 
43325                 st.store.loadData(avs);
43326                 st.collapse();
43327                 st.setValue(cn);
43328             }
43329             // flag our selected Node.
43330             this.tb.selectedNode = sel;
43331            
43332            
43333             Roo.menu.MenuMgr.hideAll();
43334
43335         }
43336         
43337         if (!updateFooter) {
43338             //this.footDisp.dom.innerHTML = ''; 
43339             return;
43340         }
43341         // update the footer
43342         //
43343         var html = '';
43344         
43345         this.footerEls = ans.reverse();
43346         Roo.each(this.footerEls, function(a,i) {
43347             if (!a) { return; }
43348             html += html.length ? ' &gt; '  :  '';
43349             
43350             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43351             
43352         });
43353        
43354         // 
43355         var sz = this.footDisp.up('td').getSize();
43356         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43357         this.footDisp.dom.style.marginLeft = '5px';
43358         
43359         this.footDisp.dom.style.overflow = 'hidden';
43360         
43361         this.footDisp.dom.innerHTML = html;
43362             
43363         //this.editorsyncValue();
43364     },
43365      
43366     
43367    
43368        
43369     // private
43370     onDestroy : function(){
43371         if(this.rendered){
43372             
43373             this.tb.items.each(function(item){
43374                 if(item.menu){
43375                     item.menu.removeAll();
43376                     if(item.menu.el){
43377                         item.menu.el.destroy();
43378                     }
43379                 }
43380                 item.destroy();
43381             });
43382              
43383         }
43384     },
43385     onFirstFocus: function() {
43386         // need to do this for all the toolbars..
43387         this.tb.items.each(function(item){
43388            item.enable();
43389         });
43390     },
43391     buildToolbar: function(tlist, nm)
43392     {
43393         var editor = this.editor;
43394          // create a new element.
43395         var wdiv = editor.wrap.createChild({
43396                 tag: 'div'
43397             }, editor.wrap.dom.firstChild.nextSibling, true);
43398         
43399        
43400         var tb = new Roo.Toolbar(wdiv);
43401         // add the name..
43402         
43403         tb.add(nm+ ":&nbsp;");
43404         
43405         var styles = [];
43406         for(var i in this.styles) {
43407             styles.push(i);
43408         }
43409         
43410         // styles...
43411         if (styles && styles.length) {
43412             
43413             // this needs a multi-select checkbox...
43414             tb.addField( new Roo.form.ComboBox({
43415                 store: new Roo.data.SimpleStore({
43416                     id : 'val',
43417                     fields: ['val', 'selected'],
43418                     data : [] 
43419                 }),
43420                 name : '-roo-edit-className',
43421                 attrname : 'className',
43422                 displayField: 'val',
43423                 typeAhead: false,
43424                 mode: 'local',
43425                 editable : false,
43426                 triggerAction: 'all',
43427                 emptyText:'Select Style',
43428                 selectOnFocus:true,
43429                 width: 130,
43430                 listeners : {
43431                     'select': function(c, r, i) {
43432                         // initial support only for on class per el..
43433                         tb.selectedNode.className =  r ? r.get('val') : '';
43434                         editor.syncValue();
43435                     }
43436                 }
43437     
43438             }));
43439         }
43440         
43441         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43442         var tbops = tbc.options;
43443         
43444         for (var i in tlist) {
43445             
43446             var item = tlist[i];
43447             tb.add(item.title + ":&nbsp;");
43448             
43449             
43450             //optname == used so you can configure the options available..
43451             var opts = item.opts ? item.opts : false;
43452             if (item.optname) {
43453                 opts = tbops[item.optname];
43454            
43455             }
43456             
43457             if (opts) {
43458                 // opts == pulldown..
43459                 tb.addField( new Roo.form.ComboBox({
43460                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43461                         id : 'val',
43462                         fields: ['val', 'display'],
43463                         data : opts  
43464                     }),
43465                     name : '-roo-edit-' + i,
43466                     attrname : i,
43467                     stylename : item.style ? item.style : false,
43468                     displayField: item.displayField ? item.displayField : 'val',
43469                     valueField :  'val',
43470                     typeAhead: false,
43471                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43472                     editable : false,
43473                     triggerAction: 'all',
43474                     emptyText:'Select',
43475                     selectOnFocus:true,
43476                     width: item.width ? item.width  : 130,
43477                     listeners : {
43478                         'select': function(c, r, i) {
43479                             if (c.stylename) {
43480                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43481                                 return;
43482                             }
43483                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43484                         }
43485                     }
43486
43487                 }));
43488                 continue;
43489                     
43490                  
43491                 
43492                 tb.addField( new Roo.form.TextField({
43493                     name: i,
43494                     width: 100,
43495                     //allowBlank:false,
43496                     value: ''
43497                 }));
43498                 continue;
43499             }
43500             tb.addField( new Roo.form.TextField({
43501                 name: '-roo-edit-' + i,
43502                 attrname : i,
43503                 
43504                 width: item.width,
43505                 //allowBlank:true,
43506                 value: '',
43507                 listeners: {
43508                     'change' : function(f, nv, ov) {
43509                         tb.selectedNode.setAttribute(f.attrname, nv);
43510                     }
43511                 }
43512             }));
43513              
43514         }
43515         tb.addFill();
43516         var _this = this;
43517         tb.addButton( {
43518             text: 'Remove Tag',
43519     
43520             listeners : {
43521                 click : function ()
43522                 {
43523                     // remove
43524                     // undo does not work.
43525                      
43526                     var sn = tb.selectedNode;
43527                     
43528                     var pn = sn.parentNode;
43529                     
43530                     var stn =  sn.childNodes[0];
43531                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43532                     while (sn.childNodes.length) {
43533                         var node = sn.childNodes[0];
43534                         sn.removeChild(node);
43535                         //Roo.log(node);
43536                         pn.insertBefore(node, sn);
43537                         
43538                     }
43539                     pn.removeChild(sn);
43540                     var range = editor.createRange();
43541         
43542                     range.setStart(stn,0);
43543                     range.setEnd(en,0); //????
43544                     //range.selectNode(sel);
43545                     
43546                     
43547                     var selection = editor.getSelection();
43548                     selection.removeAllRanges();
43549                     selection.addRange(range);
43550                     
43551                     
43552                     
43553                     //_this.updateToolbar(null, null, pn);
43554                     _this.updateToolbar(null, null, null);
43555                     _this.footDisp.dom.innerHTML = ''; 
43556                 }
43557             }
43558             
43559                     
43560                 
43561             
43562         });
43563         
43564         
43565         tb.el.on('click', function(e){
43566             e.preventDefault(); // what does this do?
43567         });
43568         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43569         tb.el.hide();
43570         tb.name = nm;
43571         // dont need to disable them... as they will get hidden
43572         return tb;
43573          
43574         
43575     },
43576     buildFooter : function()
43577     {
43578         
43579         var fel = this.editor.wrap.createChild();
43580         this.footer = new Roo.Toolbar(fel);
43581         // toolbar has scrolly on left / right?
43582         var footDisp= new Roo.Toolbar.Fill();
43583         var _t = this;
43584         this.footer.add(
43585             {
43586                 text : '&lt;',
43587                 xtype: 'Button',
43588                 handler : function() {
43589                     _t.footDisp.scrollTo('left',0,true)
43590                 }
43591             }
43592         );
43593         this.footer.add( footDisp );
43594         this.footer.add( 
43595             {
43596                 text : '&gt;',
43597                 xtype: 'Button',
43598                 handler : function() {
43599                     // no animation..
43600                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43601                 }
43602             }
43603         );
43604         var fel = Roo.get(footDisp.el);
43605         fel.addClass('x-editor-context');
43606         this.footDispWrap = fel; 
43607         this.footDispWrap.overflow  = 'hidden';
43608         
43609         this.footDisp = fel.createChild();
43610         this.footDispWrap.on('click', this.onContextClick, this)
43611         
43612         
43613     },
43614     onContextClick : function (ev,dom)
43615     {
43616         ev.preventDefault();
43617         var  cn = dom.className;
43618         //Roo.log(cn);
43619         if (!cn.match(/x-ed-loc-/)) {
43620             return;
43621         }
43622         var n = cn.split('-').pop();
43623         var ans = this.footerEls;
43624         var sel = ans[n];
43625         
43626          // pick
43627         var range = this.editor.createRange();
43628         
43629         range.selectNodeContents(sel);
43630         //range.selectNode(sel);
43631         
43632         
43633         var selection = this.editor.getSelection();
43634         selection.removeAllRanges();
43635         selection.addRange(range);
43636         
43637         
43638         
43639         this.updateToolbar(null, null, sel);
43640         
43641         
43642     }
43643     
43644     
43645     
43646     
43647     
43648 });
43649
43650
43651
43652
43653
43654 /*
43655  * Based on:
43656  * Ext JS Library 1.1.1
43657  * Copyright(c) 2006-2007, Ext JS, LLC.
43658  *
43659  * Originally Released Under LGPL - original licence link has changed is not relivant.
43660  *
43661  * Fork - LGPL
43662  * <script type="text/javascript">
43663  */
43664  
43665 /**
43666  * @class Roo.form.BasicForm
43667  * @extends Roo.util.Observable
43668  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
43669  * @constructor
43670  * @param {String/HTMLElement/Roo.Element} el The form element or its id
43671  * @param {Object} config Configuration options
43672  */
43673 Roo.form.BasicForm = function(el, config){
43674     this.allItems = [];
43675     this.childForms = [];
43676     Roo.apply(this, config);
43677     /*
43678      * The Roo.form.Field items in this form.
43679      * @type MixedCollection
43680      */
43681      
43682      
43683     this.items = new Roo.util.MixedCollection(false, function(o){
43684         return o.id || (o.id = Roo.id());
43685     });
43686     this.addEvents({
43687         /**
43688          * @event beforeaction
43689          * Fires before any action is performed. Return false to cancel the action.
43690          * @param {Form} this
43691          * @param {Action} action The action to be performed
43692          */
43693         beforeaction: true,
43694         /**
43695          * @event actionfailed
43696          * Fires when an action fails.
43697          * @param {Form} this
43698          * @param {Action} action The action that failed
43699          */
43700         actionfailed : true,
43701         /**
43702          * @event actioncomplete
43703          * Fires when an action is completed.
43704          * @param {Form} this
43705          * @param {Action} action The action that completed
43706          */
43707         actioncomplete : true
43708     });
43709     if(el){
43710         this.initEl(el);
43711     }
43712     Roo.form.BasicForm.superclass.constructor.call(this);
43713 };
43714
43715 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43716     /**
43717      * @cfg {String} method
43718      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43719      */
43720     /**
43721      * @cfg {DataReader} reader
43722      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43723      * This is optional as there is built-in support for processing JSON.
43724      */
43725     /**
43726      * @cfg {DataReader} errorReader
43727      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43728      * This is completely optional as there is built-in support for processing JSON.
43729      */
43730     /**
43731      * @cfg {String} url
43732      * The URL to use for form actions if one isn't supplied in the action options.
43733      */
43734     /**
43735      * @cfg {Boolean} fileUpload
43736      * Set to true if this form is a file upload.
43737      */
43738      
43739     /**
43740      * @cfg {Object} baseParams
43741      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43742      */
43743      /**
43744      
43745     /**
43746      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43747      */
43748     timeout: 30,
43749
43750     // private
43751     activeAction : null,
43752
43753     /**
43754      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43755      * or setValues() data instead of when the form was first created.
43756      */
43757     trackResetOnLoad : false,
43758     
43759     
43760     /**
43761      * childForms - used for multi-tab forms
43762      * @type {Array}
43763      */
43764     childForms : false,
43765     
43766     /**
43767      * allItems - full list of fields.
43768      * @type {Array}
43769      */
43770     allItems : false,
43771     
43772     /**
43773      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43774      * element by passing it or its id or mask the form itself by passing in true.
43775      * @type Mixed
43776      */
43777     waitMsgTarget : false,
43778
43779     // private
43780     initEl : function(el){
43781         this.el = Roo.get(el);
43782         this.id = this.el.id || Roo.id();
43783         this.el.on('submit', this.onSubmit, this);
43784         this.el.addClass('x-form');
43785     },
43786
43787     // private
43788     onSubmit : function(e){
43789         e.stopEvent();
43790     },
43791
43792     /**
43793      * Returns true if client-side validation on the form is successful.
43794      * @return Boolean
43795      */
43796     isValid : function(){
43797         var valid = true;
43798         this.items.each(function(f){
43799            if(!f.validate()){
43800                valid = false;
43801            }
43802         });
43803         return valid;
43804     },
43805
43806     /**
43807      * Returns true if any fields in this form have changed since their original load.
43808      * @return Boolean
43809      */
43810     isDirty : function(){
43811         var dirty = false;
43812         this.items.each(function(f){
43813            if(f.isDirty()){
43814                dirty = true;
43815                return false;
43816            }
43817         });
43818         return dirty;
43819     },
43820
43821     /**
43822      * Performs a predefined action (submit or load) or custom actions you define on this form.
43823      * @param {String} actionName The name of the action type
43824      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43825      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43826      * accept other config options):
43827      * <pre>
43828 Property          Type             Description
43829 ----------------  ---------------  ----------------------------------------------------------------------------------
43830 url               String           The url for the action (defaults to the form's url)
43831 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43832 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43833 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43834                                    validate the form on the client (defaults to false)
43835      * </pre>
43836      * @return {BasicForm} this
43837      */
43838     doAction : function(action, options){
43839         if(typeof action == 'string'){
43840             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43841         }
43842         if(this.fireEvent('beforeaction', this, action) !== false){
43843             this.beforeAction(action);
43844             action.run.defer(100, action);
43845         }
43846         return this;
43847     },
43848
43849     /**
43850      * Shortcut to do a submit action.
43851      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43852      * @return {BasicForm} this
43853      */
43854     submit : function(options){
43855         this.doAction('submit', options);
43856         return this;
43857     },
43858
43859     /**
43860      * Shortcut to do a load action.
43861      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43862      * @return {BasicForm} this
43863      */
43864     load : function(options){
43865         this.doAction('load', options);
43866         return this;
43867     },
43868
43869     /**
43870      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43871      * @param {Record} record The record to edit
43872      * @return {BasicForm} this
43873      */
43874     updateRecord : function(record){
43875         record.beginEdit();
43876         var fs = record.fields;
43877         fs.each(function(f){
43878             var field = this.findField(f.name);
43879             if(field){
43880                 record.set(f.name, field.getValue());
43881             }
43882         }, this);
43883         record.endEdit();
43884         return this;
43885     },
43886
43887     /**
43888      * Loads an Roo.data.Record into this form.
43889      * @param {Record} record The record to load
43890      * @return {BasicForm} this
43891      */
43892     loadRecord : function(record){
43893         this.setValues(record.data);
43894         return this;
43895     },
43896
43897     // private
43898     beforeAction : function(action){
43899         var o = action.options;
43900         
43901        
43902         if(this.waitMsgTarget === true){
43903             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43904         }else if(this.waitMsgTarget){
43905             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43906             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43907         }else {
43908             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43909         }
43910          
43911     },
43912
43913     // private
43914     afterAction : function(action, success){
43915         this.activeAction = null;
43916         var o = action.options;
43917         
43918         if(this.waitMsgTarget === true){
43919             this.el.unmask();
43920         }else if(this.waitMsgTarget){
43921             this.waitMsgTarget.unmask();
43922         }else{
43923             Roo.MessageBox.updateProgress(1);
43924             Roo.MessageBox.hide();
43925         }
43926          
43927         if(success){
43928             if(o.reset){
43929                 this.reset();
43930             }
43931             Roo.callback(o.success, o.scope, [this, action]);
43932             this.fireEvent('actioncomplete', this, action);
43933             
43934         }else{
43935             
43936             // failure condition..
43937             // we have a scenario where updates need confirming.
43938             // eg. if a locking scenario exists..
43939             // we look for { errors : { needs_confirm : true }} in the response.
43940             if (
43941                 (typeof(action.result) != 'undefined')  &&
43942                 (typeof(action.result.errors) != 'undefined')  &&
43943                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43944            ){
43945                 var _t = this;
43946                 Roo.MessageBox.confirm(
43947                     "Change requires confirmation",
43948                     action.result.errorMsg,
43949                     function(r) {
43950                         if (r != 'yes') {
43951                             return;
43952                         }
43953                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43954                     }
43955                     
43956                 );
43957                 
43958                 
43959                 
43960                 return;
43961             }
43962             
43963             Roo.callback(o.failure, o.scope, [this, action]);
43964             // show an error message if no failed handler is set..
43965             if (!this.hasListener('actionfailed')) {
43966                 Roo.MessageBox.alert("Error",
43967                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43968                         action.result.errorMsg :
43969                         "Saving Failed, please check your entries or try again"
43970                 );
43971             }
43972             
43973             this.fireEvent('actionfailed', this, action);
43974         }
43975         
43976     },
43977
43978     /**
43979      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43980      * @param {String} id The value to search for
43981      * @return Field
43982      */
43983     findField : function(id){
43984         var field = this.items.get(id);
43985         if(!field){
43986             this.items.each(function(f){
43987                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43988                     field = f;
43989                     return false;
43990                 }
43991             });
43992         }
43993         return field || null;
43994     },
43995
43996     /**
43997      * Add a secondary form to this one, 
43998      * Used to provide tabbed forms. One form is primary, with hidden values 
43999      * which mirror the elements from the other forms.
44000      * 
44001      * @param {Roo.form.Form} form to add.
44002      * 
44003      */
44004     addForm : function(form)
44005     {
44006        
44007         if (this.childForms.indexOf(form) > -1) {
44008             // already added..
44009             return;
44010         }
44011         this.childForms.push(form);
44012         var n = '';
44013         Roo.each(form.allItems, function (fe) {
44014             
44015             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44016             if (this.findField(n)) { // already added..
44017                 return;
44018             }
44019             var add = new Roo.form.Hidden({
44020                 name : n
44021             });
44022             add.render(this.el);
44023             
44024             this.add( add );
44025         }, this);
44026         
44027     },
44028     /**
44029      * Mark fields in this form invalid in bulk.
44030      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44031      * @return {BasicForm} this
44032      */
44033     markInvalid : function(errors){
44034         if(errors instanceof Array){
44035             for(var i = 0, len = errors.length; i < len; i++){
44036                 var fieldError = errors[i];
44037                 var f = this.findField(fieldError.id);
44038                 if(f){
44039                     f.markInvalid(fieldError.msg);
44040                 }
44041             }
44042         }else{
44043             var field, id;
44044             for(id in errors){
44045                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44046                     field.markInvalid(errors[id]);
44047                 }
44048             }
44049         }
44050         Roo.each(this.childForms || [], function (f) {
44051             f.markInvalid(errors);
44052         });
44053         
44054         return this;
44055     },
44056
44057     /**
44058      * Set values for fields in this form in bulk.
44059      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44060      * @return {BasicForm} this
44061      */
44062     setValues : function(values){
44063         if(values instanceof Array){ // array of objects
44064             for(var i = 0, len = values.length; i < len; i++){
44065                 var v = values[i];
44066                 var f = this.findField(v.id);
44067                 if(f){
44068                     f.setValue(v.value);
44069                     if(this.trackResetOnLoad){
44070                         f.originalValue = f.getValue();
44071                     }
44072                 }
44073             }
44074         }else{ // object hash
44075             var field, id;
44076             for(id in values){
44077                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44078                     
44079                     if (field.setFromData && 
44080                         field.valueField && 
44081                         field.displayField &&
44082                         // combos' with local stores can 
44083                         // be queried via setValue()
44084                         // to set their value..
44085                         (field.store && !field.store.isLocal)
44086                         ) {
44087                         // it's a combo
44088                         var sd = { };
44089                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44090                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44091                         field.setFromData(sd);
44092                         
44093                     } else {
44094                         field.setValue(values[id]);
44095                     }
44096                     
44097                     
44098                     if(this.trackResetOnLoad){
44099                         field.originalValue = field.getValue();
44100                     }
44101                 }
44102             }
44103         }
44104          
44105         Roo.each(this.childForms || [], function (f) {
44106             f.setValues(values);
44107         });
44108                 
44109         return this;
44110     },
44111
44112     /**
44113      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44114      * they are returned as an array.
44115      * @param {Boolean} asString
44116      * @return {Object}
44117      */
44118     getValues : function(asString){
44119         if (this.childForms) {
44120             // copy values from the child forms
44121             Roo.each(this.childForms, function (f) {
44122                 this.setValues(f.getValues());
44123             }, this);
44124         }
44125         
44126         
44127         
44128         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44129         if(asString === true){
44130             return fs;
44131         }
44132         return Roo.urlDecode(fs);
44133     },
44134     
44135     /**
44136      * Returns the fields in this form as an object with key/value pairs. 
44137      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44138      * @return {Object}
44139      */
44140     getFieldValues : function(with_hidden)
44141     {
44142         if (this.childForms) {
44143             // copy values from the child forms
44144             // should this call getFieldValues - probably not as we do not currently copy
44145             // hidden fields when we generate..
44146             Roo.each(this.childForms, function (f) {
44147                 this.setValues(f.getValues());
44148             }, this);
44149         }
44150         
44151         var ret = {};
44152         this.items.each(function(f){
44153             if (!f.getName()) {
44154                 return;
44155             }
44156             var v = f.getValue();
44157             if (f.inputType =='radio') {
44158                 if (typeof(ret[f.getName()]) == 'undefined') {
44159                     ret[f.getName()] = ''; // empty..
44160                 }
44161                 
44162                 if (!f.el.dom.checked) {
44163                     return;
44164                     
44165                 }
44166                 v = f.el.dom.value;
44167                 
44168             }
44169             
44170             // not sure if this supported any more..
44171             if ((typeof(v) == 'object') && f.getRawValue) {
44172                 v = f.getRawValue() ; // dates..
44173             }
44174             // combo boxes where name != hiddenName...
44175             if (f.name != f.getName()) {
44176                 ret[f.name] = f.getRawValue();
44177             }
44178             ret[f.getName()] = v;
44179         });
44180         
44181         return ret;
44182     },
44183
44184     /**
44185      * Clears all invalid messages in this form.
44186      * @return {BasicForm} this
44187      */
44188     clearInvalid : function(){
44189         this.items.each(function(f){
44190            f.clearInvalid();
44191         });
44192         
44193         Roo.each(this.childForms || [], function (f) {
44194             f.clearInvalid();
44195         });
44196         
44197         
44198         return this;
44199     },
44200
44201     /**
44202      * Resets this form.
44203      * @return {BasicForm} this
44204      */
44205     reset : function(){
44206         this.items.each(function(f){
44207             f.reset();
44208         });
44209         
44210         Roo.each(this.childForms || [], function (f) {
44211             f.reset();
44212         });
44213        
44214         
44215         return this;
44216     },
44217
44218     /**
44219      * Add Roo.form components to this form.
44220      * @param {Field} field1
44221      * @param {Field} field2 (optional)
44222      * @param {Field} etc (optional)
44223      * @return {BasicForm} this
44224      */
44225     add : function(){
44226         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44227         return this;
44228     },
44229
44230
44231     /**
44232      * Removes a field from the items collection (does NOT remove its markup).
44233      * @param {Field} field
44234      * @return {BasicForm} this
44235      */
44236     remove : function(field){
44237         this.items.remove(field);
44238         return this;
44239     },
44240
44241     /**
44242      * Looks at the fields in this form, checks them for an id attribute,
44243      * and calls applyTo on the existing dom element with that id.
44244      * @return {BasicForm} this
44245      */
44246     render : function(){
44247         this.items.each(function(f){
44248             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44249                 f.applyTo(f.id);
44250             }
44251         });
44252         return this;
44253     },
44254
44255     /**
44256      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44257      * @param {Object} values
44258      * @return {BasicForm} this
44259      */
44260     applyToFields : function(o){
44261         this.items.each(function(f){
44262            Roo.apply(f, o);
44263         });
44264         return this;
44265     },
44266
44267     /**
44268      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44269      * @param {Object} values
44270      * @return {BasicForm} this
44271      */
44272     applyIfToFields : function(o){
44273         this.items.each(function(f){
44274            Roo.applyIf(f, o);
44275         });
44276         return this;
44277     }
44278 });
44279
44280 // back compat
44281 Roo.BasicForm = Roo.form.BasicForm;/*
44282  * Based on:
44283  * Ext JS Library 1.1.1
44284  * Copyright(c) 2006-2007, Ext JS, LLC.
44285  *
44286  * Originally Released Under LGPL - original licence link has changed is not relivant.
44287  *
44288  * Fork - LGPL
44289  * <script type="text/javascript">
44290  */
44291
44292 /**
44293  * @class Roo.form.Form
44294  * @extends Roo.form.BasicForm
44295  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44296  * @constructor
44297  * @param {Object} config Configuration options
44298  */
44299 Roo.form.Form = function(config){
44300     var xitems =  [];
44301     if (config.items) {
44302         xitems = config.items;
44303         delete config.items;
44304     }
44305    
44306     
44307     Roo.form.Form.superclass.constructor.call(this, null, config);
44308     this.url = this.url || this.action;
44309     if(!this.root){
44310         this.root = new Roo.form.Layout(Roo.applyIf({
44311             id: Roo.id()
44312         }, config));
44313     }
44314     this.active = this.root;
44315     /**
44316      * Array of all the buttons that have been added to this form via {@link addButton}
44317      * @type Array
44318      */
44319     this.buttons = [];
44320     this.allItems = [];
44321     this.addEvents({
44322         /**
44323          * @event clientvalidation
44324          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44325          * @param {Form} this
44326          * @param {Boolean} valid true if the form has passed client-side validation
44327          */
44328         clientvalidation: true,
44329         /**
44330          * @event rendered
44331          * Fires when the form is rendered
44332          * @param {Roo.form.Form} form
44333          */
44334         rendered : true
44335     });
44336     
44337     if (this.progressUrl) {
44338             // push a hidden field onto the list of fields..
44339             this.addxtype( {
44340                     xns: Roo.form, 
44341                     xtype : 'Hidden', 
44342                     name : 'UPLOAD_IDENTIFIER' 
44343             });
44344         }
44345         
44346     
44347     Roo.each(xitems, this.addxtype, this);
44348     
44349     
44350     
44351 };
44352
44353 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44354     /**
44355      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44356      */
44357     /**
44358      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44359      */
44360     /**
44361      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44362      */
44363     buttonAlign:'center',
44364
44365     /**
44366      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44367      */
44368     minButtonWidth:75,
44369
44370     /**
44371      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44372      * This property cascades to child containers if not set.
44373      */
44374     labelAlign:'left',
44375
44376     /**
44377      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44378      * fires a looping event with that state. This is required to bind buttons to the valid
44379      * state using the config value formBind:true on the button.
44380      */
44381     monitorValid : false,
44382
44383     /**
44384      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44385      */
44386     monitorPoll : 200,
44387     
44388     /**
44389      * @cfg {String} progressUrl - Url to return progress data 
44390      */
44391     
44392     progressUrl : false,
44393   
44394     /**
44395      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44396      * fields are added and the column is closed. If no fields are passed the column remains open
44397      * until end() is called.
44398      * @param {Object} config The config to pass to the column
44399      * @param {Field} field1 (optional)
44400      * @param {Field} field2 (optional)
44401      * @param {Field} etc (optional)
44402      * @return Column The column container object
44403      */
44404     column : function(c){
44405         var col = new Roo.form.Column(c);
44406         this.start(col);
44407         if(arguments.length > 1){ // duplicate code required because of Opera
44408             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44409             this.end();
44410         }
44411         return col;
44412     },
44413
44414     /**
44415      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44416      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44417      * until end() is called.
44418      * @param {Object} config The config to pass to the fieldset
44419      * @param {Field} field1 (optional)
44420      * @param {Field} field2 (optional)
44421      * @param {Field} etc (optional)
44422      * @return FieldSet The fieldset container object
44423      */
44424     fieldset : function(c){
44425         var fs = new Roo.form.FieldSet(c);
44426         this.start(fs);
44427         if(arguments.length > 1){ // duplicate code required because of Opera
44428             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44429             this.end();
44430         }
44431         return fs;
44432     },
44433
44434     /**
44435      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44436      * fields are added and the container is closed. If no fields are passed the container remains open
44437      * until end() is called.
44438      * @param {Object} config The config to pass to the Layout
44439      * @param {Field} field1 (optional)
44440      * @param {Field} field2 (optional)
44441      * @param {Field} etc (optional)
44442      * @return Layout The container object
44443      */
44444     container : function(c){
44445         var l = new Roo.form.Layout(c);
44446         this.start(l);
44447         if(arguments.length > 1){ // duplicate code required because of Opera
44448             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44449             this.end();
44450         }
44451         return l;
44452     },
44453
44454     /**
44455      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44456      * @param {Object} container A Roo.form.Layout or subclass of Layout
44457      * @return {Form} this
44458      */
44459     start : function(c){
44460         // cascade label info
44461         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44462         this.active.stack.push(c);
44463         c.ownerCt = this.active;
44464         this.active = c;
44465         return this;
44466     },
44467
44468     /**
44469      * Closes the current open container
44470      * @return {Form} this
44471      */
44472     end : function(){
44473         if(this.active == this.root){
44474             return this;
44475         }
44476         this.active = this.active.ownerCt;
44477         return this;
44478     },
44479
44480     /**
44481      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44482      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44483      * as the label of the field.
44484      * @param {Field} field1
44485      * @param {Field} field2 (optional)
44486      * @param {Field} etc. (optional)
44487      * @return {Form} this
44488      */
44489     add : function(){
44490         this.active.stack.push.apply(this.active.stack, arguments);
44491         this.allItems.push.apply(this.allItems,arguments);
44492         var r = [];
44493         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44494             if(a[i].isFormField){
44495                 r.push(a[i]);
44496             }
44497         }
44498         if(r.length > 0){
44499             Roo.form.Form.superclass.add.apply(this, r);
44500         }
44501         return this;
44502     },
44503     
44504
44505     
44506     
44507     
44508      /**
44509      * Find any element that has been added to a form, using it's ID or name
44510      * This can include framesets, columns etc. along with regular fields..
44511      * @param {String} id - id or name to find.
44512      
44513      * @return {Element} e - or false if nothing found.
44514      */
44515     findbyId : function(id)
44516     {
44517         var ret = false;
44518         if (!id) {
44519             return ret;
44520         }
44521         Roo.each(this.allItems, function(f){
44522             if (f.id == id || f.name == id ){
44523                 ret = f;
44524                 return false;
44525             }
44526         });
44527         return ret;
44528     },
44529
44530     
44531     
44532     /**
44533      * Render this form into the passed container. This should only be called once!
44534      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44535      * @return {Form} this
44536      */
44537     render : function(ct)
44538     {
44539         
44540         
44541         
44542         ct = Roo.get(ct);
44543         var o = this.autoCreate || {
44544             tag: 'form',
44545             method : this.method || 'POST',
44546             id : this.id || Roo.id()
44547         };
44548         this.initEl(ct.createChild(o));
44549
44550         this.root.render(this.el);
44551         
44552        
44553              
44554         this.items.each(function(f){
44555             f.render('x-form-el-'+f.id);
44556         });
44557
44558         if(this.buttons.length > 0){
44559             // tables are required to maintain order and for correct IE layout
44560             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44561                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44562                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44563             }}, null, true);
44564             var tr = tb.getElementsByTagName('tr')[0];
44565             for(var i = 0, len = this.buttons.length; i < len; i++) {
44566                 var b = this.buttons[i];
44567                 var td = document.createElement('td');
44568                 td.className = 'x-form-btn-td';
44569                 b.render(tr.appendChild(td));
44570             }
44571         }
44572         if(this.monitorValid){ // initialize after render
44573             this.startMonitoring();
44574         }
44575         this.fireEvent('rendered', this);
44576         return this;
44577     },
44578
44579     /**
44580      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44581      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44582      * object or a valid Roo.DomHelper element config
44583      * @param {Function} handler The function called when the button is clicked
44584      * @param {Object} scope (optional) The scope of the handler function
44585      * @return {Roo.Button}
44586      */
44587     addButton : function(config, handler, scope){
44588         var bc = {
44589             handler: handler,
44590             scope: scope,
44591             minWidth: this.minButtonWidth,
44592             hideParent:true
44593         };
44594         if(typeof config == "string"){
44595             bc.text = config;
44596         }else{
44597             Roo.apply(bc, config);
44598         }
44599         var btn = new Roo.Button(null, bc);
44600         this.buttons.push(btn);
44601         return btn;
44602     },
44603
44604      /**
44605      * Adds a series of form elements (using the xtype property as the factory method.
44606      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44607      * @param {Object} config 
44608      */
44609     
44610     addxtype : function()
44611     {
44612         var ar = Array.prototype.slice.call(arguments, 0);
44613         var ret = false;
44614         for(var i = 0; i < ar.length; i++) {
44615             if (!ar[i]) {
44616                 continue; // skip -- if this happends something invalid got sent, we 
44617                 // should ignore it, as basically that interface element will not show up
44618                 // and that should be pretty obvious!!
44619             }
44620             
44621             if (Roo.form[ar[i].xtype]) {
44622                 ar[i].form = this;
44623                 var fe = Roo.factory(ar[i], Roo.form);
44624                 if (!ret) {
44625                     ret = fe;
44626                 }
44627                 fe.form = this;
44628                 if (fe.store) {
44629                     fe.store.form = this;
44630                 }
44631                 if (fe.isLayout) {  
44632                          
44633                     this.start(fe);
44634                     this.allItems.push(fe);
44635                     if (fe.items && fe.addxtype) {
44636                         fe.addxtype.apply(fe, fe.items);
44637                         delete fe.items;
44638                     }
44639                      this.end();
44640                     continue;
44641                 }
44642                 
44643                 
44644                  
44645                 this.add(fe);
44646               //  console.log('adding ' + ar[i].xtype);
44647             }
44648             if (ar[i].xtype == 'Button') {  
44649                 //console.log('adding button');
44650                 //console.log(ar[i]);
44651                 this.addButton(ar[i]);
44652                 this.allItems.push(fe);
44653                 continue;
44654             }
44655             
44656             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
44657                 alert('end is not supported on xtype any more, use items');
44658             //    this.end();
44659             //    //console.log('adding end');
44660             }
44661             
44662         }
44663         return ret;
44664     },
44665     
44666     /**
44667      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
44668      * option "monitorValid"
44669      */
44670     startMonitoring : function(){
44671         if(!this.bound){
44672             this.bound = true;
44673             Roo.TaskMgr.start({
44674                 run : this.bindHandler,
44675                 interval : this.monitorPoll || 200,
44676                 scope: this
44677             });
44678         }
44679     },
44680
44681     /**
44682      * Stops monitoring of the valid state of this form
44683      */
44684     stopMonitoring : function(){
44685         this.bound = false;
44686     },
44687
44688     // private
44689     bindHandler : function(){
44690         if(!this.bound){
44691             return false; // stops binding
44692         }
44693         var valid = true;
44694         this.items.each(function(f){
44695             if(!f.isValid(true)){
44696                 valid = false;
44697                 return false;
44698             }
44699         });
44700         for(var i = 0, len = this.buttons.length; i < len; i++){
44701             var btn = this.buttons[i];
44702             if(btn.formBind === true && btn.disabled === valid){
44703                 btn.setDisabled(!valid);
44704             }
44705         }
44706         this.fireEvent('clientvalidation', this, valid);
44707     }
44708     
44709     
44710     
44711     
44712     
44713     
44714     
44715     
44716 });
44717
44718
44719 // back compat
44720 Roo.Form = Roo.form.Form;
44721 /*
44722  * Based on:
44723  * Ext JS Library 1.1.1
44724  * Copyright(c) 2006-2007, Ext JS, LLC.
44725  *
44726  * Originally Released Under LGPL - original licence link has changed is not relivant.
44727  *
44728  * Fork - LGPL
44729  * <script type="text/javascript">
44730  */
44731
44732 // as we use this in bootstrap.
44733 Roo.namespace('Roo.form');
44734  /**
44735  * @class Roo.form.Action
44736  * Internal Class used to handle form actions
44737  * @constructor
44738  * @param {Roo.form.BasicForm} el The form element or its id
44739  * @param {Object} config Configuration options
44740  */
44741
44742  
44743  
44744 // define the action interface
44745 Roo.form.Action = function(form, options){
44746     this.form = form;
44747     this.options = options || {};
44748 };
44749 /**
44750  * Client Validation Failed
44751  * @const 
44752  */
44753 Roo.form.Action.CLIENT_INVALID = 'client';
44754 /**
44755  * Server Validation Failed
44756  * @const 
44757  */
44758 Roo.form.Action.SERVER_INVALID = 'server';
44759  /**
44760  * Connect to Server Failed
44761  * @const 
44762  */
44763 Roo.form.Action.CONNECT_FAILURE = 'connect';
44764 /**
44765  * Reading Data from Server Failed
44766  * @const 
44767  */
44768 Roo.form.Action.LOAD_FAILURE = 'load';
44769
44770 Roo.form.Action.prototype = {
44771     type : 'default',
44772     failureType : undefined,
44773     response : undefined,
44774     result : undefined,
44775
44776     // interface method
44777     run : function(options){
44778
44779     },
44780
44781     // interface method
44782     success : function(response){
44783
44784     },
44785
44786     // interface method
44787     handleResponse : function(response){
44788
44789     },
44790
44791     // default connection failure
44792     failure : function(response){
44793         
44794         this.response = response;
44795         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44796         this.form.afterAction(this, false);
44797     },
44798
44799     processResponse : function(response){
44800         this.response = response;
44801         if(!response.responseText){
44802             return true;
44803         }
44804         this.result = this.handleResponse(response);
44805         return this.result;
44806     },
44807
44808     // utility functions used internally
44809     getUrl : function(appendParams){
44810         var url = this.options.url || this.form.url || this.form.el.dom.action;
44811         if(appendParams){
44812             var p = this.getParams();
44813             if(p){
44814                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44815             }
44816         }
44817         return url;
44818     },
44819
44820     getMethod : function(){
44821         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44822     },
44823
44824     getParams : function(){
44825         var bp = this.form.baseParams;
44826         var p = this.options.params;
44827         if(p){
44828             if(typeof p == "object"){
44829                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44830             }else if(typeof p == 'string' && bp){
44831                 p += '&' + Roo.urlEncode(bp);
44832             }
44833         }else if(bp){
44834             p = Roo.urlEncode(bp);
44835         }
44836         return p;
44837     },
44838
44839     createCallback : function(){
44840         return {
44841             success: this.success,
44842             failure: this.failure,
44843             scope: this,
44844             timeout: (this.form.timeout*1000),
44845             upload: this.form.fileUpload ? this.success : undefined
44846         };
44847     }
44848 };
44849
44850 Roo.form.Action.Submit = function(form, options){
44851     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44852 };
44853
44854 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44855     type : 'submit',
44856
44857     haveProgress : false,
44858     uploadComplete : false,
44859     
44860     // uploadProgress indicator.
44861     uploadProgress : function()
44862     {
44863         if (!this.form.progressUrl) {
44864             return;
44865         }
44866         
44867         if (!this.haveProgress) {
44868             Roo.MessageBox.progress("Uploading", "Uploading");
44869         }
44870         if (this.uploadComplete) {
44871            Roo.MessageBox.hide();
44872            return;
44873         }
44874         
44875         this.haveProgress = true;
44876    
44877         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44878         
44879         var c = new Roo.data.Connection();
44880         c.request({
44881             url : this.form.progressUrl,
44882             params: {
44883                 id : uid
44884             },
44885             method: 'GET',
44886             success : function(req){
44887                //console.log(data);
44888                 var rdata = false;
44889                 var edata;
44890                 try  {
44891                    rdata = Roo.decode(req.responseText)
44892                 } catch (e) {
44893                     Roo.log("Invalid data from server..");
44894                     Roo.log(edata);
44895                     return;
44896                 }
44897                 if (!rdata || !rdata.success) {
44898                     Roo.log(rdata);
44899                     Roo.MessageBox.alert(Roo.encode(rdata));
44900                     return;
44901                 }
44902                 var data = rdata.data;
44903                 
44904                 if (this.uploadComplete) {
44905                    Roo.MessageBox.hide();
44906                    return;
44907                 }
44908                    
44909                 if (data){
44910                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44911                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44912                     );
44913                 }
44914                 this.uploadProgress.defer(2000,this);
44915             },
44916        
44917             failure: function(data) {
44918                 Roo.log('progress url failed ');
44919                 Roo.log(data);
44920             },
44921             scope : this
44922         });
44923            
44924     },
44925     
44926     
44927     run : function()
44928     {
44929         // run get Values on the form, so it syncs any secondary forms.
44930         this.form.getValues();
44931         
44932         var o = this.options;
44933         var method = this.getMethod();
44934         var isPost = method == 'POST';
44935         if(o.clientValidation === false || this.form.isValid()){
44936             
44937             if (this.form.progressUrl) {
44938                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44939                     (new Date() * 1) + '' + Math.random());
44940                     
44941             } 
44942             
44943             
44944             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44945                 form:this.form.el.dom,
44946                 url:this.getUrl(!isPost),
44947                 method: method,
44948                 params:isPost ? this.getParams() : null,
44949                 isUpload: this.form.fileUpload
44950             }));
44951             
44952             this.uploadProgress();
44953
44954         }else if (o.clientValidation !== false){ // client validation failed
44955             this.failureType = Roo.form.Action.CLIENT_INVALID;
44956             this.form.afterAction(this, false);
44957         }
44958     },
44959
44960     success : function(response)
44961     {
44962         this.uploadComplete= true;
44963         if (this.haveProgress) {
44964             Roo.MessageBox.hide();
44965         }
44966         
44967         
44968         var result = this.processResponse(response);
44969         if(result === true || result.success){
44970             this.form.afterAction(this, true);
44971             return;
44972         }
44973         if(result.errors){
44974             this.form.markInvalid(result.errors);
44975             this.failureType = Roo.form.Action.SERVER_INVALID;
44976         }
44977         this.form.afterAction(this, false);
44978     },
44979     failure : function(response)
44980     {
44981         this.uploadComplete= true;
44982         if (this.haveProgress) {
44983             Roo.MessageBox.hide();
44984         }
44985         
44986         this.response = response;
44987         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44988         this.form.afterAction(this, false);
44989     },
44990     
44991     handleResponse : function(response){
44992         if(this.form.errorReader){
44993             var rs = this.form.errorReader.read(response);
44994             var errors = [];
44995             if(rs.records){
44996                 for(var i = 0, len = rs.records.length; i < len; i++) {
44997                     var r = rs.records[i];
44998                     errors[i] = r.data;
44999                 }
45000             }
45001             if(errors.length < 1){
45002                 errors = null;
45003             }
45004             return {
45005                 success : rs.success,
45006                 errors : errors
45007             };
45008         }
45009         var ret = false;
45010         try {
45011             ret = Roo.decode(response.responseText);
45012         } catch (e) {
45013             ret = {
45014                 success: false,
45015                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45016                 errors : []
45017             };
45018         }
45019         return ret;
45020         
45021     }
45022 });
45023
45024
45025 Roo.form.Action.Load = function(form, options){
45026     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45027     this.reader = this.form.reader;
45028 };
45029
45030 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45031     type : 'load',
45032
45033     run : function(){
45034         
45035         Roo.Ajax.request(Roo.apply(
45036                 this.createCallback(), {
45037                     method:this.getMethod(),
45038                     url:this.getUrl(false),
45039                     params:this.getParams()
45040         }));
45041     },
45042
45043     success : function(response){
45044         
45045         var result = this.processResponse(response);
45046         if(result === true || !result.success || !result.data){
45047             this.failureType = Roo.form.Action.LOAD_FAILURE;
45048             this.form.afterAction(this, false);
45049             return;
45050         }
45051         this.form.clearInvalid();
45052         this.form.setValues(result.data);
45053         this.form.afterAction(this, true);
45054     },
45055
45056     handleResponse : function(response){
45057         if(this.form.reader){
45058             var rs = this.form.reader.read(response);
45059             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45060             return {
45061                 success : rs.success,
45062                 data : data
45063             };
45064         }
45065         return Roo.decode(response.responseText);
45066     }
45067 });
45068
45069 Roo.form.Action.ACTION_TYPES = {
45070     'load' : Roo.form.Action.Load,
45071     'submit' : Roo.form.Action.Submit
45072 };/*
45073  * Based on:
45074  * Ext JS Library 1.1.1
45075  * Copyright(c) 2006-2007, Ext JS, LLC.
45076  *
45077  * Originally Released Under LGPL - original licence link has changed is not relivant.
45078  *
45079  * Fork - LGPL
45080  * <script type="text/javascript">
45081  */
45082  
45083 /**
45084  * @class Roo.form.Layout
45085  * @extends Roo.Component
45086  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45087  * @constructor
45088  * @param {Object} config Configuration options
45089  */
45090 Roo.form.Layout = function(config){
45091     var xitems = [];
45092     if (config.items) {
45093         xitems = config.items;
45094         delete config.items;
45095     }
45096     Roo.form.Layout.superclass.constructor.call(this, config);
45097     this.stack = [];
45098     Roo.each(xitems, this.addxtype, this);
45099      
45100 };
45101
45102 Roo.extend(Roo.form.Layout, Roo.Component, {
45103     /**
45104      * @cfg {String/Object} autoCreate
45105      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45106      */
45107     /**
45108      * @cfg {String/Object/Function} style
45109      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45110      * a function which returns such a specification.
45111      */
45112     /**
45113      * @cfg {String} labelAlign
45114      * Valid values are "left," "top" and "right" (defaults to "left")
45115      */
45116     /**
45117      * @cfg {Number} labelWidth
45118      * Fixed width in pixels of all field labels (defaults to undefined)
45119      */
45120     /**
45121      * @cfg {Boolean} clear
45122      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45123      */
45124     clear : true,
45125     /**
45126      * @cfg {String} labelSeparator
45127      * The separator to use after field labels (defaults to ':')
45128      */
45129     labelSeparator : ':',
45130     /**
45131      * @cfg {Boolean} hideLabels
45132      * True to suppress the display of field labels in this layout (defaults to false)
45133      */
45134     hideLabels : false,
45135
45136     // private
45137     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45138     
45139     isLayout : true,
45140     
45141     // private
45142     onRender : function(ct, position){
45143         if(this.el){ // from markup
45144             this.el = Roo.get(this.el);
45145         }else {  // generate
45146             var cfg = this.getAutoCreate();
45147             this.el = ct.createChild(cfg, position);
45148         }
45149         if(this.style){
45150             this.el.applyStyles(this.style);
45151         }
45152         if(this.labelAlign){
45153             this.el.addClass('x-form-label-'+this.labelAlign);
45154         }
45155         if(this.hideLabels){
45156             this.labelStyle = "display:none";
45157             this.elementStyle = "padding-left:0;";
45158         }else{
45159             if(typeof this.labelWidth == 'number'){
45160                 this.labelStyle = "width:"+this.labelWidth+"px;";
45161                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45162             }
45163             if(this.labelAlign == 'top'){
45164                 this.labelStyle = "width:auto;";
45165                 this.elementStyle = "padding-left:0;";
45166             }
45167         }
45168         var stack = this.stack;
45169         var slen = stack.length;
45170         if(slen > 0){
45171             if(!this.fieldTpl){
45172                 var t = new Roo.Template(
45173                     '<div class="x-form-item {5}">',
45174                         '<label for="{0}" style="{2}">{1}{4}</label>',
45175                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45176                         '</div>',
45177                     '</div><div class="x-form-clear-left"></div>'
45178                 );
45179                 t.disableFormats = true;
45180                 t.compile();
45181                 Roo.form.Layout.prototype.fieldTpl = t;
45182             }
45183             for(var i = 0; i < slen; i++) {
45184                 if(stack[i].isFormField){
45185                     this.renderField(stack[i]);
45186                 }else{
45187                     this.renderComponent(stack[i]);
45188                 }
45189             }
45190         }
45191         if(this.clear){
45192             this.el.createChild({cls:'x-form-clear'});
45193         }
45194     },
45195
45196     // private
45197     renderField : function(f){
45198         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45199                f.id, //0
45200                f.fieldLabel, //1
45201                f.labelStyle||this.labelStyle||'', //2
45202                this.elementStyle||'', //3
45203                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45204                f.itemCls||this.itemCls||''  //5
45205        ], true).getPrevSibling());
45206     },
45207
45208     // private
45209     renderComponent : function(c){
45210         c.render(c.isLayout ? this.el : this.el.createChild());    
45211     },
45212     /**
45213      * Adds a object form elements (using the xtype property as the factory method.)
45214      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45215      * @param {Object} config 
45216      */
45217     addxtype : function(o)
45218     {
45219         // create the lement.
45220         o.form = this.form;
45221         var fe = Roo.factory(o, Roo.form);
45222         this.form.allItems.push(fe);
45223         this.stack.push(fe);
45224         
45225         if (fe.isFormField) {
45226             this.form.items.add(fe);
45227         }
45228          
45229         return fe;
45230     }
45231 });
45232
45233 /**
45234  * @class Roo.form.Column
45235  * @extends Roo.form.Layout
45236  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45237  * @constructor
45238  * @param {Object} config Configuration options
45239  */
45240 Roo.form.Column = function(config){
45241     Roo.form.Column.superclass.constructor.call(this, config);
45242 };
45243
45244 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45245     /**
45246      * @cfg {Number/String} width
45247      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45248      */
45249     /**
45250      * @cfg {String/Object} autoCreate
45251      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45252      */
45253
45254     // private
45255     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45256
45257     // private
45258     onRender : function(ct, position){
45259         Roo.form.Column.superclass.onRender.call(this, ct, position);
45260         if(this.width){
45261             this.el.setWidth(this.width);
45262         }
45263     }
45264 });
45265
45266
45267 /**
45268  * @class Roo.form.Row
45269  * @extends Roo.form.Layout
45270  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45271  * @constructor
45272  * @param {Object} config Configuration options
45273  */
45274
45275  
45276 Roo.form.Row = function(config){
45277     Roo.form.Row.superclass.constructor.call(this, config);
45278 };
45279  
45280 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45281       /**
45282      * @cfg {Number/String} width
45283      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45284      */
45285     /**
45286      * @cfg {Number/String} height
45287      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45288      */
45289     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45290     
45291     padWidth : 20,
45292     // private
45293     onRender : function(ct, position){
45294         //console.log('row render');
45295         if(!this.rowTpl){
45296             var t = new Roo.Template(
45297                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45298                     '<label for="{0}" style="{2}">{1}{4}</label>',
45299                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45300                     '</div>',
45301                 '</div>'
45302             );
45303             t.disableFormats = true;
45304             t.compile();
45305             Roo.form.Layout.prototype.rowTpl = t;
45306         }
45307         this.fieldTpl = this.rowTpl;
45308         
45309         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45310         var labelWidth = 100;
45311         
45312         if ((this.labelAlign != 'top')) {
45313             if (typeof this.labelWidth == 'number') {
45314                 labelWidth = this.labelWidth
45315             }
45316             this.padWidth =  20 + labelWidth;
45317             
45318         }
45319         
45320         Roo.form.Column.superclass.onRender.call(this, ct, position);
45321         if(this.width){
45322             this.el.setWidth(this.width);
45323         }
45324         if(this.height){
45325             this.el.setHeight(this.height);
45326         }
45327     },
45328     
45329     // private
45330     renderField : function(f){
45331         f.fieldEl = this.fieldTpl.append(this.el, [
45332                f.id, f.fieldLabel,
45333                f.labelStyle||this.labelStyle||'',
45334                this.elementStyle||'',
45335                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45336                f.itemCls||this.itemCls||'',
45337                f.width ? f.width + this.padWidth : 160 + this.padWidth
45338        ],true);
45339     }
45340 });
45341  
45342
45343 /**
45344  * @class Roo.form.FieldSet
45345  * @extends Roo.form.Layout
45346  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45347  * @constructor
45348  * @param {Object} config Configuration options
45349  */
45350 Roo.form.FieldSet = function(config){
45351     Roo.form.FieldSet.superclass.constructor.call(this, config);
45352 };
45353
45354 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45355     /**
45356      * @cfg {String} legend
45357      * The text to display as the legend for the FieldSet (defaults to '')
45358      */
45359     /**
45360      * @cfg {String/Object} autoCreate
45361      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45362      */
45363
45364     // private
45365     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45366
45367     // private
45368     onRender : function(ct, position){
45369         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45370         if(this.legend){
45371             this.setLegend(this.legend);
45372         }
45373     },
45374
45375     // private
45376     setLegend : function(text){
45377         if(this.rendered){
45378             this.el.child('legend').update(text);
45379         }
45380     }
45381 });/*
45382  * Based on:
45383  * Ext JS Library 1.1.1
45384  * Copyright(c) 2006-2007, Ext JS, LLC.
45385  *
45386  * Originally Released Under LGPL - original licence link has changed is not relivant.
45387  *
45388  * Fork - LGPL
45389  * <script type="text/javascript">
45390  */
45391 /**
45392  * @class Roo.form.VTypes
45393  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45394  * @singleton
45395  */
45396 Roo.form.VTypes = function(){
45397     // closure these in so they are only created once.
45398     var alpha = /^[a-zA-Z_]+$/;
45399     var alphanum = /^[a-zA-Z0-9_]+$/;
45400     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45401     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45402
45403     // All these messages and functions are configurable
45404     return {
45405         /**
45406          * The function used to validate email addresses
45407          * @param {String} value The email address
45408          */
45409         'email' : function(v){
45410             return email.test(v);
45411         },
45412         /**
45413          * The error text to display when the email validation function returns false
45414          * @type String
45415          */
45416         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45417         /**
45418          * The keystroke filter mask to be applied on email input
45419          * @type RegExp
45420          */
45421         'emailMask' : /[a-z0-9_\.\-@]/i,
45422
45423         /**
45424          * The function used to validate URLs
45425          * @param {String} value The URL
45426          */
45427         'url' : function(v){
45428             return url.test(v);
45429         },
45430         /**
45431          * The error text to display when the url validation function returns false
45432          * @type String
45433          */
45434         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45435         
45436         /**
45437          * The function used to validate alpha values
45438          * @param {String} value The value
45439          */
45440         'alpha' : function(v){
45441             return alpha.test(v);
45442         },
45443         /**
45444          * The error text to display when the alpha validation function returns false
45445          * @type String
45446          */
45447         'alphaText' : 'This field should only contain letters and _',
45448         /**
45449          * The keystroke filter mask to be applied on alpha input
45450          * @type RegExp
45451          */
45452         'alphaMask' : /[a-z_]/i,
45453
45454         /**
45455          * The function used to validate alphanumeric values
45456          * @param {String} value The value
45457          */
45458         'alphanum' : function(v){
45459             return alphanum.test(v);
45460         },
45461         /**
45462          * The error text to display when the alphanumeric validation function returns false
45463          * @type String
45464          */
45465         'alphanumText' : 'This field should only contain letters, numbers and _',
45466         /**
45467          * The keystroke filter mask to be applied on alphanumeric input
45468          * @type RegExp
45469          */
45470         'alphanumMask' : /[a-z0-9_]/i
45471     };
45472 }();//<script type="text/javascript">
45473
45474 /**
45475  * @class Roo.form.FCKeditor
45476  * @extends Roo.form.TextArea
45477  * Wrapper around the FCKEditor http://www.fckeditor.net
45478  * @constructor
45479  * Creates a new FCKeditor
45480  * @param {Object} config Configuration options
45481  */
45482 Roo.form.FCKeditor = function(config){
45483     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45484     this.addEvents({
45485          /**
45486          * @event editorinit
45487          * Fired when the editor is initialized - you can add extra handlers here..
45488          * @param {FCKeditor} this
45489          * @param {Object} the FCK object.
45490          */
45491         editorinit : true
45492     });
45493     
45494     
45495 };
45496 Roo.form.FCKeditor.editors = { };
45497 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45498 {
45499     //defaultAutoCreate : {
45500     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45501     //},
45502     // private
45503     /**
45504      * @cfg {Object} fck options - see fck manual for details.
45505      */
45506     fckconfig : false,
45507     
45508     /**
45509      * @cfg {Object} fck toolbar set (Basic or Default)
45510      */
45511     toolbarSet : 'Basic',
45512     /**
45513      * @cfg {Object} fck BasePath
45514      */ 
45515     basePath : '/fckeditor/',
45516     
45517     
45518     frame : false,
45519     
45520     value : '',
45521     
45522    
45523     onRender : function(ct, position)
45524     {
45525         if(!this.el){
45526             this.defaultAutoCreate = {
45527                 tag: "textarea",
45528                 style:"width:300px;height:60px;",
45529                 autocomplete: "off"
45530             };
45531         }
45532         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45533         /*
45534         if(this.grow){
45535             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45536             if(this.preventScrollbars){
45537                 this.el.setStyle("overflow", "hidden");
45538             }
45539             this.el.setHeight(this.growMin);
45540         }
45541         */
45542         //console.log('onrender' + this.getId() );
45543         Roo.form.FCKeditor.editors[this.getId()] = this;
45544          
45545
45546         this.replaceTextarea() ;
45547         
45548     },
45549     
45550     getEditor : function() {
45551         return this.fckEditor;
45552     },
45553     /**
45554      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45555      * @param {Mixed} value The value to set
45556      */
45557     
45558     
45559     setValue : function(value)
45560     {
45561         //console.log('setValue: ' + value);
45562         
45563         if(typeof(value) == 'undefined') { // not sure why this is happending...
45564             return;
45565         }
45566         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45567         
45568         //if(!this.el || !this.getEditor()) {
45569         //    this.value = value;
45570             //this.setValue.defer(100,this,[value]);    
45571         //    return;
45572         //} 
45573         
45574         if(!this.getEditor()) {
45575             return;
45576         }
45577         
45578         this.getEditor().SetData(value);
45579         
45580         //
45581
45582     },
45583
45584     /**
45585      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45586      * @return {Mixed} value The field value
45587      */
45588     getValue : function()
45589     {
45590         
45591         if (this.frame && this.frame.dom.style.display == 'none') {
45592             return Roo.form.FCKeditor.superclass.getValue.call(this);
45593         }
45594         
45595         if(!this.el || !this.getEditor()) {
45596            
45597            // this.getValue.defer(100,this); 
45598             return this.value;
45599         }
45600        
45601         
45602         var value=this.getEditor().GetData();
45603         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45604         return Roo.form.FCKeditor.superclass.getValue.call(this);
45605         
45606
45607     },
45608
45609     /**
45610      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45611      * @return {Mixed} value The field value
45612      */
45613     getRawValue : function()
45614     {
45615         if (this.frame && this.frame.dom.style.display == 'none') {
45616             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45617         }
45618         
45619         if(!this.el || !this.getEditor()) {
45620             //this.getRawValue.defer(100,this); 
45621             return this.value;
45622             return;
45623         }
45624         
45625         
45626         
45627         var value=this.getEditor().GetData();
45628         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45629         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45630          
45631     },
45632     
45633     setSize : function(w,h) {
45634         
45635         
45636         
45637         //if (this.frame && this.frame.dom.style.display == 'none') {
45638         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45639         //    return;
45640         //}
45641         //if(!this.el || !this.getEditor()) {
45642         //    this.setSize.defer(100,this, [w,h]); 
45643         //    return;
45644         //}
45645         
45646         
45647         
45648         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45649         
45650         this.frame.dom.setAttribute('width', w);
45651         this.frame.dom.setAttribute('height', h);
45652         this.frame.setSize(w,h);
45653         
45654     },
45655     
45656     toggleSourceEdit : function(value) {
45657         
45658       
45659          
45660         this.el.dom.style.display = value ? '' : 'none';
45661         this.frame.dom.style.display = value ?  'none' : '';
45662         
45663     },
45664     
45665     
45666     focus: function(tag)
45667     {
45668         if (this.frame.dom.style.display == 'none') {
45669             return Roo.form.FCKeditor.superclass.focus.call(this);
45670         }
45671         if(!this.el || !this.getEditor()) {
45672             this.focus.defer(100,this, [tag]); 
45673             return;
45674         }
45675         
45676         
45677         
45678         
45679         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
45680         this.getEditor().Focus();
45681         if (tgs.length) {
45682             if (!this.getEditor().Selection.GetSelection()) {
45683                 this.focus.defer(100,this, [tag]); 
45684                 return;
45685             }
45686             
45687             
45688             var r = this.getEditor().EditorDocument.createRange();
45689             r.setStart(tgs[0],0);
45690             r.setEnd(tgs[0],0);
45691             this.getEditor().Selection.GetSelection().removeAllRanges();
45692             this.getEditor().Selection.GetSelection().addRange(r);
45693             this.getEditor().Focus();
45694         }
45695         
45696     },
45697     
45698     
45699     
45700     replaceTextarea : function()
45701     {
45702         if ( document.getElementById( this.getId() + '___Frame' ) )
45703             return ;
45704         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
45705         //{
45706             // We must check the elements firstly using the Id and then the name.
45707         var oTextarea = document.getElementById( this.getId() );
45708         
45709         var colElementsByName = document.getElementsByName( this.getId() ) ;
45710          
45711         oTextarea.style.display = 'none' ;
45712
45713         if ( oTextarea.tabIndex ) {            
45714             this.TabIndex = oTextarea.tabIndex ;
45715         }
45716         
45717         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45718         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45719         this.frame = Roo.get(this.getId() + '___Frame')
45720     },
45721     
45722     _getConfigHtml : function()
45723     {
45724         var sConfig = '' ;
45725
45726         for ( var o in this.fckconfig ) {
45727             sConfig += sConfig.length > 0  ? '&amp;' : '';
45728             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45729         }
45730
45731         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45732     },
45733     
45734     
45735     _getIFrameHtml : function()
45736     {
45737         var sFile = 'fckeditor.html' ;
45738         /* no idea what this is about..
45739         try
45740         {
45741             if ( (/fcksource=true/i).test( window.top.location.search ) )
45742                 sFile = 'fckeditor.original.html' ;
45743         }
45744         catch (e) { 
45745         */
45746
45747         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45748         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45749         
45750         
45751         var html = '<iframe id="' + this.getId() +
45752             '___Frame" src="' + sLink +
45753             '" width="' + this.width +
45754             '" height="' + this.height + '"' +
45755             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45756             ' frameborder="0" scrolling="no"></iframe>' ;
45757
45758         return html ;
45759     },
45760     
45761     _insertHtmlBefore : function( html, element )
45762     {
45763         if ( element.insertAdjacentHTML )       {
45764             // IE
45765             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45766         } else { // Gecko
45767             var oRange = document.createRange() ;
45768             oRange.setStartBefore( element ) ;
45769             var oFragment = oRange.createContextualFragment( html );
45770             element.parentNode.insertBefore( oFragment, element ) ;
45771         }
45772     }
45773     
45774     
45775   
45776     
45777     
45778     
45779     
45780
45781 });
45782
45783 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45784
45785 function FCKeditor_OnComplete(editorInstance){
45786     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45787     f.fckEditor = editorInstance;
45788     //console.log("loaded");
45789     f.fireEvent('editorinit', f, editorInstance);
45790
45791   
45792
45793  
45794
45795
45796
45797
45798
45799
45800
45801
45802
45803
45804
45805
45806
45807
45808
45809 //<script type="text/javascript">
45810 /**
45811  * @class Roo.form.GridField
45812  * @extends Roo.form.Field
45813  * Embed a grid (or editable grid into a form)
45814  * STATUS ALPHA
45815  * 
45816  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45817  * it needs 
45818  * xgrid.store = Roo.data.Store
45819  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45820  * xgrid.store.reader = Roo.data.JsonReader 
45821  * 
45822  * 
45823  * @constructor
45824  * Creates a new GridField
45825  * @param {Object} config Configuration options
45826  */
45827 Roo.form.GridField = function(config){
45828     Roo.form.GridField.superclass.constructor.call(this, config);
45829      
45830 };
45831
45832 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45833     /**
45834      * @cfg {Number} width  - used to restrict width of grid..
45835      */
45836     width : 100,
45837     /**
45838      * @cfg {Number} height - used to restrict height of grid..
45839      */
45840     height : 50,
45841      /**
45842      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45843          * 
45844          *}
45845      */
45846     xgrid : false, 
45847     /**
45848      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45849      * {tag: "input", type: "checkbox", autocomplete: "off"})
45850      */
45851    // defaultAutoCreate : { tag: 'div' },
45852     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45853     /**
45854      * @cfg {String} addTitle Text to include for adding a title.
45855      */
45856     addTitle : false,
45857     //
45858     onResize : function(){
45859         Roo.form.Field.superclass.onResize.apply(this, arguments);
45860     },
45861
45862     initEvents : function(){
45863         // Roo.form.Checkbox.superclass.initEvents.call(this);
45864         // has no events...
45865        
45866     },
45867
45868
45869     getResizeEl : function(){
45870         return this.wrap;
45871     },
45872
45873     getPositionEl : function(){
45874         return this.wrap;
45875     },
45876
45877     // private
45878     onRender : function(ct, position){
45879         
45880         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45881         var style = this.style;
45882         delete this.style;
45883         
45884         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45885         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45886         this.viewEl = this.wrap.createChild({ tag: 'div' });
45887         if (style) {
45888             this.viewEl.applyStyles(style);
45889         }
45890         if (this.width) {
45891             this.viewEl.setWidth(this.width);
45892         }
45893         if (this.height) {
45894             this.viewEl.setHeight(this.height);
45895         }
45896         //if(this.inputValue !== undefined){
45897         //this.setValue(this.value);
45898         
45899         
45900         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45901         
45902         
45903         this.grid.render();
45904         this.grid.getDataSource().on('remove', this.refreshValue, this);
45905         this.grid.getDataSource().on('update', this.refreshValue, this);
45906         this.grid.on('afteredit', this.refreshValue, this);
45907  
45908     },
45909      
45910     
45911     /**
45912      * Sets the value of the item. 
45913      * @param {String} either an object  or a string..
45914      */
45915     setValue : function(v){
45916         //this.value = v;
45917         v = v || []; // empty set..
45918         // this does not seem smart - it really only affects memoryproxy grids..
45919         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45920             var ds = this.grid.getDataSource();
45921             // assumes a json reader..
45922             var data = {}
45923             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45924             ds.loadData( data);
45925         }
45926         // clear selection so it does not get stale.
45927         if (this.grid.sm) { 
45928             this.grid.sm.clearSelections();
45929         }
45930         
45931         Roo.form.GridField.superclass.setValue.call(this, v);
45932         this.refreshValue();
45933         // should load data in the grid really....
45934     },
45935     
45936     // private
45937     refreshValue: function() {
45938          var val = [];
45939         this.grid.getDataSource().each(function(r) {
45940             val.push(r.data);
45941         });
45942         this.el.dom.value = Roo.encode(val);
45943     }
45944     
45945      
45946     
45947     
45948 });/*
45949  * Based on:
45950  * Ext JS Library 1.1.1
45951  * Copyright(c) 2006-2007, Ext JS, LLC.
45952  *
45953  * Originally Released Under LGPL - original licence link has changed is not relivant.
45954  *
45955  * Fork - LGPL
45956  * <script type="text/javascript">
45957  */
45958 /**
45959  * @class Roo.form.DisplayField
45960  * @extends Roo.form.Field
45961  * A generic Field to display non-editable data.
45962  * @constructor
45963  * Creates a new Display Field item.
45964  * @param {Object} config Configuration options
45965  */
45966 Roo.form.DisplayField = function(config){
45967     Roo.form.DisplayField.superclass.constructor.call(this, config);
45968     
45969 };
45970
45971 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45972     inputType:      'hidden',
45973     allowBlank:     true,
45974     readOnly:         true,
45975     
45976  
45977     /**
45978      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45979      */
45980     focusClass : undefined,
45981     /**
45982      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45983      */
45984     fieldClass: 'x-form-field',
45985     
45986      /**
45987      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45988      */
45989     valueRenderer: undefined,
45990     
45991     width: 100,
45992     /**
45993      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45994      * {tag: "input", type: "checkbox", autocomplete: "off"})
45995      */
45996      
45997  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45998
45999     onResize : function(){
46000         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46001         
46002     },
46003
46004     initEvents : function(){
46005         // Roo.form.Checkbox.superclass.initEvents.call(this);
46006         // has no events...
46007        
46008     },
46009
46010
46011     getResizeEl : function(){
46012         return this.wrap;
46013     },
46014
46015     getPositionEl : function(){
46016         return this.wrap;
46017     },
46018
46019     // private
46020     onRender : function(ct, position){
46021         
46022         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46023         //if(this.inputValue !== undefined){
46024         this.wrap = this.el.wrap();
46025         
46026         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46027         
46028         if (this.bodyStyle) {
46029             this.viewEl.applyStyles(this.bodyStyle);
46030         }
46031         //this.viewEl.setStyle('padding', '2px');
46032         
46033         this.setValue(this.value);
46034         
46035     },
46036 /*
46037     // private
46038     initValue : Roo.emptyFn,
46039
46040   */
46041
46042         // private
46043     onClick : function(){
46044         
46045     },
46046
46047     /**
46048      * Sets the checked state of the checkbox.
46049      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46050      */
46051     setValue : function(v){
46052         this.value = v;
46053         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46054         // this might be called before we have a dom element..
46055         if (!this.viewEl) {
46056             return;
46057         }
46058         this.viewEl.dom.innerHTML = html;
46059         Roo.form.DisplayField.superclass.setValue.call(this, v);
46060
46061     }
46062 });/*
46063  * 
46064  * Licence- LGPL
46065  * 
46066  */
46067
46068 /**
46069  * @class Roo.form.DayPicker
46070  * @extends Roo.form.Field
46071  * A Day picker show [M] [T] [W] ....
46072  * @constructor
46073  * Creates a new Day Picker
46074  * @param {Object} config Configuration options
46075  */
46076 Roo.form.DayPicker= function(config){
46077     Roo.form.DayPicker.superclass.constructor.call(this, config);
46078      
46079 };
46080
46081 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46082     /**
46083      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46084      */
46085     focusClass : undefined,
46086     /**
46087      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46088      */
46089     fieldClass: "x-form-field",
46090    
46091     /**
46092      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46093      * {tag: "input", type: "checkbox", autocomplete: "off"})
46094      */
46095     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46096     
46097    
46098     actionMode : 'viewEl', 
46099     //
46100     // private
46101  
46102     inputType : 'hidden',
46103     
46104      
46105     inputElement: false, // real input element?
46106     basedOn: false, // ????
46107     
46108     isFormField: true, // not sure where this is needed!!!!
46109
46110     onResize : function(){
46111         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46112         if(!this.boxLabel){
46113             this.el.alignTo(this.wrap, 'c-c');
46114         }
46115     },
46116
46117     initEvents : function(){
46118         Roo.form.Checkbox.superclass.initEvents.call(this);
46119         this.el.on("click", this.onClick,  this);
46120         this.el.on("change", this.onClick,  this);
46121     },
46122
46123
46124     getResizeEl : function(){
46125         return this.wrap;
46126     },
46127
46128     getPositionEl : function(){
46129         return this.wrap;
46130     },
46131
46132     
46133     // private
46134     onRender : function(ct, position){
46135         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46136        
46137         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46138         
46139         var r1 = '<table><tr>';
46140         var r2 = '<tr class="x-form-daypick-icons">';
46141         for (var i=0; i < 7; i++) {
46142             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46143             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46144         }
46145         
46146         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46147         viewEl.select('img').on('click', this.onClick, this);
46148         this.viewEl = viewEl;   
46149         
46150         
46151         // this will not work on Chrome!!!
46152         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46153         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46154         
46155         
46156           
46157
46158     },
46159
46160     // private
46161     initValue : Roo.emptyFn,
46162
46163     /**
46164      * Returns the checked state of the checkbox.
46165      * @return {Boolean} True if checked, else false
46166      */
46167     getValue : function(){
46168         return this.el.dom.value;
46169         
46170     },
46171
46172         // private
46173     onClick : function(e){ 
46174         //this.setChecked(!this.checked);
46175         Roo.get(e.target).toggleClass('x-menu-item-checked');
46176         this.refreshValue();
46177         //if(this.el.dom.checked != this.checked){
46178         //    this.setValue(this.el.dom.checked);
46179        // }
46180     },
46181     
46182     // private
46183     refreshValue : function()
46184     {
46185         var val = '';
46186         this.viewEl.select('img',true).each(function(e,i,n)  {
46187             val += e.is(".x-menu-item-checked") ? String(n) : '';
46188         });
46189         this.setValue(val, true);
46190     },
46191
46192     /**
46193      * Sets the checked state of the checkbox.
46194      * On is always based on a string comparison between inputValue and the param.
46195      * @param {Boolean/String} value - the value to set 
46196      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46197      */
46198     setValue : function(v,suppressEvent){
46199         if (!this.el.dom) {
46200             return;
46201         }
46202         var old = this.el.dom.value ;
46203         this.el.dom.value = v;
46204         if (suppressEvent) {
46205             return ;
46206         }
46207          
46208         // update display..
46209         this.viewEl.select('img',true).each(function(e,i,n)  {
46210             
46211             var on = e.is(".x-menu-item-checked");
46212             var newv = v.indexOf(String(n)) > -1;
46213             if (on != newv) {
46214                 e.toggleClass('x-menu-item-checked');
46215             }
46216             
46217         });
46218         
46219         
46220         this.fireEvent('change', this, v, old);
46221         
46222         
46223     },
46224    
46225     // handle setting of hidden value by some other method!!?!?
46226     setFromHidden: function()
46227     {
46228         if(!this.el){
46229             return;
46230         }
46231         //console.log("SET FROM HIDDEN");
46232         //alert('setFrom hidden');
46233         this.setValue(this.el.dom.value);
46234     },
46235     
46236     onDestroy : function()
46237     {
46238         if(this.viewEl){
46239             Roo.get(this.viewEl).remove();
46240         }
46241          
46242         Roo.form.DayPicker.superclass.onDestroy.call(this);
46243     }
46244
46245 });/*
46246  * RooJS Library 1.1.1
46247  * Copyright(c) 2008-2011  Alan Knowles
46248  *
46249  * License - LGPL
46250  */
46251  
46252
46253 /**
46254  * @class Roo.form.ComboCheck
46255  * @extends Roo.form.ComboBox
46256  * A combobox for multiple select items.
46257  *
46258  * FIXME - could do with a reset button..
46259  * 
46260  * @constructor
46261  * Create a new ComboCheck
46262  * @param {Object} config Configuration options
46263  */
46264 Roo.form.ComboCheck = function(config){
46265     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46266     // should verify some data...
46267     // like
46268     // hiddenName = required..
46269     // displayField = required
46270     // valudField == required
46271     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46272     var _t = this;
46273     Roo.each(req, function(e) {
46274         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46275             throw "Roo.form.ComboCheck : missing value for: " + e;
46276         }
46277     });
46278     
46279     
46280 };
46281
46282 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46283      
46284      
46285     editable : false,
46286      
46287     selectedClass: 'x-menu-item-checked', 
46288     
46289     // private
46290     onRender : function(ct, position){
46291         var _t = this;
46292         
46293         
46294         
46295         if(!this.tpl){
46296             var cls = 'x-combo-list';
46297
46298             
46299             this.tpl =  new Roo.Template({
46300                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46301                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46302                    '<span>{' + this.displayField + '}</span>' +
46303                     '</div>' 
46304                 
46305             });
46306         }
46307  
46308         
46309         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46310         this.view.singleSelect = false;
46311         this.view.multiSelect = true;
46312         this.view.toggleSelect = true;
46313         this.pageTb.add(new Roo.Toolbar.Fill(), {
46314             
46315             text: 'Done',
46316             handler: function()
46317             {
46318                 _t.collapse();
46319             }
46320         });
46321     },
46322     
46323     onViewOver : function(e, t){
46324         // do nothing...
46325         return;
46326         
46327     },
46328     
46329     onViewClick : function(doFocus,index){
46330         return;
46331         
46332     },
46333     select: function () {
46334         //Roo.log("SELECT CALLED");
46335     },
46336      
46337     selectByValue : function(xv, scrollIntoView){
46338         var ar = this.getValueArray();
46339         var sels = [];
46340         
46341         Roo.each(ar, function(v) {
46342             if(v === undefined || v === null){
46343                 return;
46344             }
46345             var r = this.findRecord(this.valueField, v);
46346             if(r){
46347                 sels.push(this.store.indexOf(r))
46348                 
46349             }
46350         },this);
46351         this.view.select(sels);
46352         return false;
46353     },
46354     
46355     
46356     
46357     onSelect : function(record, index){
46358        // Roo.log("onselect Called");
46359        // this is only called by the clear button now..
46360         this.view.clearSelections();
46361         this.setValue('[]');
46362         if (this.value != this.valueBefore) {
46363             this.fireEvent('change', this, this.value, this.valueBefore);
46364             this.valueBefore = this.value;
46365         }
46366     },
46367     getValueArray : function()
46368     {
46369         var ar = [] ;
46370         
46371         try {
46372             //Roo.log(this.value);
46373             if (typeof(this.value) == 'undefined') {
46374                 return [];
46375             }
46376             var ar = Roo.decode(this.value);
46377             return  ar instanceof Array ? ar : []; //?? valid?
46378             
46379         } catch(e) {
46380             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46381             return [];
46382         }
46383          
46384     },
46385     expand : function ()
46386     {
46387         
46388         Roo.form.ComboCheck.superclass.expand.call(this);
46389         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46390         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46391         
46392
46393     },
46394     
46395     collapse : function(){
46396         Roo.form.ComboCheck.superclass.collapse.call(this);
46397         var sl = this.view.getSelectedIndexes();
46398         var st = this.store;
46399         var nv = [];
46400         var tv = [];
46401         var r;
46402         Roo.each(sl, function(i) {
46403             r = st.getAt(i);
46404             nv.push(r.get(this.valueField));
46405         },this);
46406         this.setValue(Roo.encode(nv));
46407         if (this.value != this.valueBefore) {
46408
46409             this.fireEvent('change', this, this.value, this.valueBefore);
46410             this.valueBefore = this.value;
46411         }
46412         
46413     },
46414     
46415     setValue : function(v){
46416         // Roo.log(v);
46417         this.value = v;
46418         
46419         var vals = this.getValueArray();
46420         var tv = [];
46421         Roo.each(vals, function(k) {
46422             var r = this.findRecord(this.valueField, k);
46423             if(r){
46424                 tv.push(r.data[this.displayField]);
46425             }else if(this.valueNotFoundText !== undefined){
46426                 tv.push( this.valueNotFoundText );
46427             }
46428         },this);
46429        // Roo.log(tv);
46430         
46431         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46432         this.hiddenField.value = v;
46433         this.value = v;
46434     }
46435     
46436 });/*
46437  * Based on:
46438  * Ext JS Library 1.1.1
46439  * Copyright(c) 2006-2007, Ext JS, LLC.
46440  *
46441  * Originally Released Under LGPL - original licence link has changed is not relivant.
46442  *
46443  * Fork - LGPL
46444  * <script type="text/javascript">
46445  */
46446  
46447 /**
46448  * @class Roo.form.Signature
46449  * @extends Roo.form.Field
46450  * Signature field.  
46451  * @constructor
46452  * 
46453  * @param {Object} config Configuration options
46454  */
46455
46456 Roo.form.Signature = function(config){
46457     Roo.form.Signature.superclass.constructor.call(this, config);
46458     
46459     this.addEvents({// not in used??
46460          /**
46461          * @event confirm
46462          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46463              * @param {Roo.form.Signature} combo This combo box
46464              */
46465         'confirm' : true,
46466         /**
46467          * @event reset
46468          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46469              * @param {Roo.form.ComboBox} combo This combo box
46470              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46471              */
46472         'reset' : true
46473     });
46474 };
46475
46476 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46477     /**
46478      * @cfg {Object} labels Label to use when rendering a form.
46479      * defaults to 
46480      * labels : { 
46481      *      clear : "Clear",
46482      *      confirm : "Confirm"
46483      *  }
46484      */
46485     labels : { 
46486         clear : "Clear",
46487         confirm : "Confirm"
46488     },
46489     /**
46490      * @cfg {Number} width The signature panel width (defaults to 300)
46491      */
46492     width: 300,
46493     /**
46494      * @cfg {Number} height The signature panel height (defaults to 100)
46495      */
46496     height : 100,
46497     /**
46498      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46499      */
46500     allowBlank : false,
46501     
46502     //private
46503     // {Object} signPanel The signature SVG panel element (defaults to {})
46504     signPanel : {},
46505     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46506     isMouseDown : false,
46507     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46508     isConfirmed : false,
46509     // {String} signatureTmp SVG mapping string (defaults to empty string)
46510     signatureTmp : '',
46511     
46512     
46513     defaultAutoCreate : { // modified by initCompnoent..
46514         tag: "input",
46515         type:"hidden"
46516     },
46517
46518     // private
46519     onRender : function(ct, position){
46520         
46521         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46522         
46523         this.wrap = this.el.wrap({
46524             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46525         });
46526         
46527         this.createToolbar(this);
46528         this.signPanel = this.wrap.createChild({
46529                 tag: 'div',
46530                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46531             }, this.el
46532         );
46533             
46534         this.svgID = Roo.id();
46535         this.svgEl = this.signPanel.createChild({
46536               xmlns : 'http://www.w3.org/2000/svg',
46537               tag : 'svg',
46538               id : this.svgID + "-svg",
46539               width: this.width,
46540               height: this.height,
46541               viewBox: '0 0 '+this.width+' '+this.height,
46542               cn : [
46543                 {
46544                     tag: "rect",
46545                     id: this.svgID + "-svg-r",
46546                     width: this.width,
46547                     height: this.height,
46548                     fill: "#ffa"
46549                 },
46550                 {
46551                     tag: "line",
46552                     id: this.svgID + "-svg-l",
46553                     x1: "0", // start
46554                     y1: (this.height*0.8), // start set the line in 80% of height
46555                     x2: this.width, // end
46556                     y2: (this.height*0.8), // end set the line in 80% of height
46557                     'stroke': "#666",
46558                     'stroke-width': "1",
46559                     'stroke-dasharray': "3",
46560                     'shape-rendering': "crispEdges",
46561                     'pointer-events': "none"
46562                 },
46563                 {
46564                     tag: "path",
46565                     id: this.svgID + "-svg-p",
46566                     'stroke': "navy",
46567                     'stroke-width': "3",
46568                     'fill': "none",
46569                     'pointer-events': 'none'
46570                 }
46571               ]
46572         });
46573         this.createSVG();
46574         this.svgBox = this.svgEl.dom.getScreenCTM();
46575     },
46576     createSVG : function(){ 
46577         var svg = this.signPanel;
46578         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46579         var t = this;
46580
46581         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46582         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46583         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46584         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46585         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46586         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46587         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46588         
46589     },
46590     isTouchEvent : function(e){
46591         return e.type.match(/^touch/);
46592     },
46593     getCoords : function (e) {
46594         var pt    = this.svgEl.dom.createSVGPoint();
46595         pt.x = e.clientX; 
46596         pt.y = e.clientY;
46597         if (this.isTouchEvent(e)) {
46598             pt.x =  e.targetTouches[0].clientX 
46599             pt.y = e.targetTouches[0].clientY;
46600         }
46601         var a = this.svgEl.dom.getScreenCTM();
46602         var b = a.inverse();
46603         var mx = pt.matrixTransform(b);
46604         return mx.x + ',' + mx.y;
46605     },
46606     //mouse event headler 
46607     down : function (e) {
46608         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46609         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46610         
46611         this.isMouseDown = true;
46612         
46613         e.preventDefault();
46614     },
46615     move : function (e) {
46616         if (this.isMouseDown) {
46617             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46618             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46619         }
46620         
46621         e.preventDefault();
46622     },
46623     up : function (e) {
46624         this.isMouseDown = false;
46625         var sp = this.signatureTmp.split(' ');
46626         
46627         if(sp.length > 1){
46628             if(!sp[sp.length-2].match(/^L/)){
46629                 sp.pop();
46630                 sp.pop();
46631                 sp.push("");
46632                 this.signatureTmp = sp.join(" ");
46633             }
46634         }
46635         if(this.getValue() != this.signatureTmp){
46636             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46637             this.isConfirmed = false;
46638         }
46639         e.preventDefault();
46640     },
46641     
46642     /**
46643      * Protected method that will not generally be called directly. It
46644      * is called when the editor creates its toolbar. Override this method if you need to
46645      * add custom toolbar buttons.
46646      * @param {HtmlEditor} editor
46647      */
46648     createToolbar : function(editor){
46649          function btn(id, toggle, handler){
46650             var xid = fid + '-'+ id ;
46651             return {
46652                 id : xid,
46653                 cmd : id,
46654                 cls : 'x-btn-icon x-edit-'+id,
46655                 enableToggle:toggle !== false,
46656                 scope: editor, // was editor...
46657                 handler:handler||editor.relayBtnCmd,
46658                 clickEvent:'mousedown',
46659                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46660                 tabIndex:-1
46661             };
46662         }
46663         
46664         
46665         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46666         this.tb = tb;
46667         this.tb.add(
46668            {
46669                 cls : ' x-signature-btn x-signature-'+id,
46670                 scope: editor, // was editor...
46671                 handler: this.reset,
46672                 clickEvent:'mousedown',
46673                 text: this.labels.clear
46674             },
46675             {
46676                  xtype : 'Fill',
46677                  xns: Roo.Toolbar
46678             }, 
46679             {
46680                 cls : '  x-signature-btn x-signature-'+id,
46681                 scope: editor, // was editor...
46682                 handler: this.confirmHandler,
46683                 clickEvent:'mousedown',
46684                 text: this.labels.confirm
46685             }
46686         );
46687     
46688     },
46689     //public
46690     /**
46691      * when user is clicked confirm then show this image.....
46692      * 
46693      * @return {String} Image Data URI
46694      */
46695     getImageDataURI : function(){
46696         var svg = this.svgEl.dom.parentNode.innerHTML;
46697         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
46698         return src; 
46699     },
46700     /**
46701      * 
46702      * @return {Boolean} this.isConfirmed
46703      */
46704     getConfirmed : function(){
46705         return this.isConfirmed;
46706     },
46707     /**
46708      * 
46709      * @return {Number} this.width
46710      */
46711     getWidth : function(){
46712         return this.width;
46713     },
46714     /**
46715      * 
46716      * @return {Number} this.height
46717      */
46718     getHeight : function(){
46719         return this.height;
46720     },
46721     // private
46722     getSignature : function(){
46723         return this.signatureTmp;
46724     },
46725     // private
46726     reset : function(){
46727         this.signatureTmp = '';
46728         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46729         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46730         this.isConfirmed = false;
46731         Roo.form.Signature.superclass.reset.call(this);
46732     },
46733     setSignature : function(s){
46734         this.signatureTmp = s;
46735         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46736         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46737         this.setValue(s);
46738         this.isConfirmed = false;
46739         Roo.form.Signature.superclass.reset.call(this);
46740     }, 
46741     test : function(){
46742 //        Roo.log(this.signPanel.dom.contentWindow.up())
46743     },
46744     //private
46745     setConfirmed : function(){
46746         
46747         
46748         
46749 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46750     },
46751     // private
46752     confirmHandler : function(){
46753         if(!this.getSignature()){
46754             return;
46755         }
46756         
46757         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46758         this.setValue(this.getSignature());
46759         this.isConfirmed = true;
46760         
46761         this.fireEvent('confirm', this);
46762     },
46763     // private
46764     // Subclasses should provide the validation implementation by overriding this
46765     validateValue : function(value){
46766         if(this.allowBlank){
46767             return true;
46768         }
46769         
46770         if(this.isConfirmed){
46771             return true;
46772         }
46773         return false;
46774     }
46775 });/*
46776  * Based on:
46777  * Ext JS Library 1.1.1
46778  * Copyright(c) 2006-2007, Ext JS, LLC.
46779  *
46780  * Originally Released Under LGPL - original licence link has changed is not relivant.
46781  *
46782  * Fork - LGPL
46783  * <script type="text/javascript">
46784  */
46785  
46786
46787 /**
46788  * @class Roo.form.ComboBox
46789  * @extends Roo.form.TriggerField
46790  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
46791  * @constructor
46792  * Create a new ComboBox.
46793  * @param {Object} config Configuration options
46794  */
46795 Roo.form.Select = function(config){
46796     Roo.form.Select.superclass.constructor.call(this, config);
46797      
46798 };
46799
46800 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
46801     /**
46802      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
46803      */
46804     /**
46805      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
46806      * rendering into an Roo.Editor, defaults to false)
46807      */
46808     /**
46809      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
46810      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
46811      */
46812     /**
46813      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
46814      */
46815     /**
46816      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
46817      * the dropdown list (defaults to undefined, with no header element)
46818      */
46819
46820      /**
46821      * @cfg {String/Roo.Template} tpl The template to use to render the output
46822      */
46823      
46824     // private
46825     defaultAutoCreate : {tag: "select"  },
46826     /**
46827      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
46828      */
46829     listWidth: undefined,
46830     /**
46831      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
46832      * mode = 'remote' or 'text' if mode = 'local')
46833      */
46834     displayField: undefined,
46835     /**
46836      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
46837      * mode = 'remote' or 'value' if mode = 'local'). 
46838      * Note: use of a valueField requires the user make a selection
46839      * in order for a value to be mapped.
46840      */
46841     valueField: undefined,
46842     
46843     
46844     /**
46845      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
46846      * field's data value (defaults to the underlying DOM element's name)
46847      */
46848     hiddenName: undefined,
46849     /**
46850      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
46851      */
46852     listClass: '',
46853     /**
46854      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
46855      */
46856     selectedClass: 'x-combo-selected',
46857     /**
46858      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
46859      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
46860      * which displays a downward arrow icon).
46861      */
46862     triggerClass : 'x-form-arrow-trigger',
46863     /**
46864      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
46865      */
46866     shadow:'sides',
46867     /**
46868      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
46869      * anchor positions (defaults to 'tl-bl')
46870      */
46871     listAlign: 'tl-bl?',
46872     /**
46873      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
46874      */
46875     maxHeight: 300,
46876     /**
46877      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
46878      * query specified by the allQuery config option (defaults to 'query')
46879      */
46880     triggerAction: 'query',
46881     /**
46882      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
46883      * (defaults to 4, does not apply if editable = false)
46884      */
46885     minChars : 4,
46886     /**
46887      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
46888      * delay (typeAheadDelay) if it matches a known value (defaults to false)
46889      */
46890     typeAhead: false,
46891     /**
46892      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
46893      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
46894      */
46895     queryDelay: 500,
46896     /**
46897      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
46898      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
46899      */
46900     pageSize: 0,
46901     /**
46902      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
46903      * when editable = true (defaults to false)
46904      */
46905     selectOnFocus:false,
46906     /**
46907      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
46908      */
46909     queryParam: 'query',
46910     /**
46911      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
46912      * when mode = 'remote' (defaults to 'Loading...')
46913      */
46914     loadingText: 'Loading...',
46915     /**
46916      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
46917      */
46918     resizable: false,
46919     /**
46920      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
46921      */
46922     handleHeight : 8,
46923     /**
46924      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
46925      * traditional select (defaults to true)
46926      */
46927     editable: true,
46928     /**
46929      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
46930      */
46931     allQuery: '',
46932     /**
46933      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
46934      */
46935     mode: 'remote',
46936     /**
46937      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
46938      * listWidth has a higher value)
46939      */
46940     minListWidth : 70,
46941     /**
46942      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
46943      * allow the user to set arbitrary text into the field (defaults to false)
46944      */
46945     forceSelection:false,
46946     /**
46947      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
46948      * if typeAhead = true (defaults to 250)
46949      */
46950     typeAheadDelay : 250,
46951     /**
46952      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
46953      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
46954      */
46955     valueNotFoundText : undefined,
46956     
46957     /**
46958      * @cfg {String} defaultValue The value displayed after loading the store.
46959      */
46960     defaultValue: '',
46961     
46962     /**
46963      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
46964      */
46965     blockFocus : false,
46966     
46967     /**
46968      * @cfg {Boolean} disableClear Disable showing of clear button.
46969      */
46970     disableClear : false,
46971     /**
46972      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
46973      */
46974     alwaysQuery : false,
46975     
46976     //private
46977     addicon : false,
46978     editicon: false,
46979     
46980     // element that contains real text value.. (when hidden is used..)
46981      
46982     // private
46983     onRender : function(ct, position){
46984         Roo.form.Field.prototype.onRender.call(this, ct, position);
46985         
46986         if(this.store){
46987             this.store.on('beforeload', this.onBeforeLoad, this);
46988             this.store.on('load', this.onLoad, this);
46989             this.store.on('loadexception', this.onLoadException, this);
46990             this.store.load({});
46991         }
46992         
46993         
46994         
46995     },
46996
46997     // private
46998     initEvents : function(){
46999         //Roo.form.ComboBox.superclass.initEvents.call(this);
47000  
47001     },
47002
47003     onDestroy : function(){
47004        
47005         if(this.store){
47006             this.store.un('beforeload', this.onBeforeLoad, this);
47007             this.store.un('load', this.onLoad, this);
47008             this.store.un('loadexception', this.onLoadException, this);
47009         }
47010         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47011     },
47012
47013     // private
47014     fireKey : function(e){
47015         if(e.isNavKeyPress() && !this.list.isVisible()){
47016             this.fireEvent("specialkey", this, e);
47017         }
47018     },
47019
47020     // private
47021     onResize: function(w, h){
47022         
47023         return; 
47024     
47025         
47026     },
47027
47028     /**
47029      * Allow or prevent the user from directly editing the field text.  If false is passed,
47030      * the user will only be able to select from the items defined in the dropdown list.  This method
47031      * is the runtime equivalent of setting the 'editable' config option at config time.
47032      * @param {Boolean} value True to allow the user to directly edit the field text
47033      */
47034     setEditable : function(value){
47035          
47036     },
47037
47038     // private
47039     onBeforeLoad : function(){
47040         
47041         Roo.log("Select before load");
47042         return;
47043     
47044         this.innerList.update(this.loadingText ?
47045                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47046         //this.restrictHeight();
47047         this.selectedIndex = -1;
47048     },
47049
47050     // private
47051     onLoad : function(){
47052
47053     
47054         var dom = this.el.dom;
47055         dom.innerHTML = '';
47056          var od = dom.ownerDocument;
47057          
47058         if (this.emptyText) {
47059             var op = od.createElement('option');
47060             op.setAttribute('value', '');
47061             op.innerHTML = String.format('{0}', this.emptyText);
47062             dom.appendChild(op);
47063         }
47064         if(this.store.getCount() > 0){
47065            
47066             var vf = this.valueField;
47067             var df = this.displayField;
47068             this.store.data.each(function(r) {
47069                 // which colmsn to use... testing - cdoe / title..
47070                 var op = od.createElement('option');
47071                 op.setAttribute('value', r.data[vf]);
47072                 op.innerHTML = String.format('{0}', r.data[df]);
47073                 dom.appendChild(op);
47074             });
47075             if (typeof(this.defaultValue != 'undefined')) {
47076                 this.setValue(this.defaultValue);
47077             }
47078             
47079              
47080         }else{
47081             //this.onEmptyResults();
47082         }
47083         //this.el.focus();
47084     },
47085     // private
47086     onLoadException : function()
47087     {
47088         dom.innerHTML = '';
47089             
47090         Roo.log("Select on load exception");
47091         return;
47092     
47093         this.collapse();
47094         Roo.log(this.store.reader.jsonData);
47095         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47096             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47097         }
47098         
47099         
47100     },
47101     // private
47102     onTypeAhead : function(){
47103          
47104     },
47105
47106     // private
47107     onSelect : function(record, index){
47108         Roo.log('on select?');
47109         return;
47110         if(this.fireEvent('beforeselect', this, record, index) !== false){
47111             this.setFromData(index > -1 ? record.data : false);
47112             this.collapse();
47113             this.fireEvent('select', this, record, index);
47114         }
47115     },
47116
47117     /**
47118      * Returns the currently selected field value or empty string if no value is set.
47119      * @return {String} value The selected value
47120      */
47121     getValue : function(){
47122         var dom = this.el.dom;
47123         this.value = dom.options[dom.selectedIndex].value;
47124         return this.value;
47125         
47126     },
47127
47128     /**
47129      * Clears any text/value currently set in the field
47130      */
47131     clearValue : function(){
47132         this.value = '';
47133         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47134         
47135     },
47136
47137     /**
47138      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47139      * will be displayed in the field.  If the value does not match the data value of an existing item,
47140      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47141      * Otherwise the field will be blank (although the value will still be set).
47142      * @param {String} value The value to match
47143      */
47144     setValue : function(v){
47145         var d = this.el.dom;
47146         for (var i =0; i < d.options.length;i++) {
47147             if (v == d.options[i].value) {
47148                 d.selectedIndex = i;
47149                 this.value = v;
47150                 return;
47151             }
47152         }
47153         this.clearValue();
47154     },
47155     /**
47156      * @property {Object} the last set data for the element
47157      */
47158     
47159     lastData : false,
47160     /**
47161      * Sets the value of the field based on a object which is related to the record format for the store.
47162      * @param {Object} value the value to set as. or false on reset?
47163      */
47164     setFromData : function(o){
47165         Roo.log('setfrom data?');
47166          
47167         
47168         
47169     },
47170     // private
47171     reset : function(){
47172         this.clearValue();
47173     },
47174     // private
47175     findRecord : function(prop, value){
47176         
47177         return false;
47178     
47179         var record;
47180         if(this.store.getCount() > 0){
47181             this.store.each(function(r){
47182                 if(r.data[prop] == value){
47183                     record = r;
47184                     return false;
47185                 }
47186                 return true;
47187             });
47188         }
47189         return record;
47190     },
47191     
47192     getName: function()
47193     {
47194         // returns hidden if it's set..
47195         if (!this.rendered) {return ''};
47196         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47197         
47198     },
47199      
47200
47201     
47202
47203     // private
47204     onEmptyResults : function(){
47205         Roo.log('empty results');
47206         //this.collapse();
47207     },
47208
47209     /**
47210      * Returns true if the dropdown list is expanded, else false.
47211      */
47212     isExpanded : function(){
47213         return false;
47214     },
47215
47216     /**
47217      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47218      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47219      * @param {String} value The data value of the item to select
47220      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47221      * selected item if it is not currently in view (defaults to true)
47222      * @return {Boolean} True if the value matched an item in the list, else false
47223      */
47224     selectByValue : function(v, scrollIntoView){
47225         Roo.log('select By Value');
47226         return false;
47227     
47228         if(v !== undefined && v !== null){
47229             var r = this.findRecord(this.valueField || this.displayField, v);
47230             if(r){
47231                 this.select(this.store.indexOf(r), scrollIntoView);
47232                 return true;
47233             }
47234         }
47235         return false;
47236     },
47237
47238     /**
47239      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47240      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47241      * @param {Number} index The zero-based index of the list item to select
47242      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47243      * selected item if it is not currently in view (defaults to true)
47244      */
47245     select : function(index, scrollIntoView){
47246         Roo.log('select ');
47247         return  ;
47248         
47249         this.selectedIndex = index;
47250         this.view.select(index);
47251         if(scrollIntoView !== false){
47252             var el = this.view.getNode(index);
47253             if(el){
47254                 this.innerList.scrollChildIntoView(el, false);
47255             }
47256         }
47257     },
47258
47259       
47260
47261     // private
47262     validateBlur : function(){
47263         
47264         return;
47265         
47266     },
47267
47268     // private
47269     initQuery : function(){
47270         this.doQuery(this.getRawValue());
47271     },
47272
47273     // private
47274     doForce : function(){
47275         if(this.el.dom.value.length > 0){
47276             this.el.dom.value =
47277                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47278              
47279         }
47280     },
47281
47282     /**
47283      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47284      * query allowing the query action to be canceled if needed.
47285      * @param {String} query The SQL query to execute
47286      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47287      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47288      * saved in the current store (defaults to false)
47289      */
47290     doQuery : function(q, forceAll){
47291         
47292         Roo.log('doQuery?');
47293         if(q === undefined || q === null){
47294             q = '';
47295         }
47296         var qe = {
47297             query: q,
47298             forceAll: forceAll,
47299             combo: this,
47300             cancel:false
47301         };
47302         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47303             return false;
47304         }
47305         q = qe.query;
47306         forceAll = qe.forceAll;
47307         if(forceAll === true || (q.length >= this.minChars)){
47308             if(this.lastQuery != q || this.alwaysQuery){
47309                 this.lastQuery = q;
47310                 if(this.mode == 'local'){
47311                     this.selectedIndex = -1;
47312                     if(forceAll){
47313                         this.store.clearFilter();
47314                     }else{
47315                         this.store.filter(this.displayField, q);
47316                     }
47317                     this.onLoad();
47318                 }else{
47319                     this.store.baseParams[this.queryParam] = q;
47320                     this.store.load({
47321                         params: this.getParams(q)
47322                     });
47323                     this.expand();
47324                 }
47325             }else{
47326                 this.selectedIndex = -1;
47327                 this.onLoad();   
47328             }
47329         }
47330     },
47331
47332     // private
47333     getParams : function(q){
47334         var p = {};
47335         //p[this.queryParam] = q;
47336         if(this.pageSize){
47337             p.start = 0;
47338             p.limit = this.pageSize;
47339         }
47340         return p;
47341     },
47342
47343     /**
47344      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47345      */
47346     collapse : function(){
47347         
47348     },
47349
47350     // private
47351     collapseIf : function(e){
47352         
47353     },
47354
47355     /**
47356      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47357      */
47358     expand : function(){
47359         
47360     } ,
47361
47362     // private
47363      
47364
47365     /** 
47366     * @cfg {Boolean} grow 
47367     * @hide 
47368     */
47369     /** 
47370     * @cfg {Number} growMin 
47371     * @hide 
47372     */
47373     /** 
47374     * @cfg {Number} growMax 
47375     * @hide 
47376     */
47377     /**
47378      * @hide
47379      * @method autoSize
47380      */
47381     
47382     setWidth : function()
47383     {
47384         
47385     },
47386     getResizeEl : function(){
47387         return this.el;
47388     }
47389 });//<script type="text/javasscript">
47390  
47391
47392 /**
47393  * @class Roo.DDView
47394  * A DnD enabled version of Roo.View.
47395  * @param {Element/String} container The Element in which to create the View.
47396  * @param {String} tpl The template string used to create the markup for each element of the View
47397  * @param {Object} config The configuration properties. These include all the config options of
47398  * {@link Roo.View} plus some specific to this class.<br>
47399  * <p>
47400  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47401  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47402  * <p>
47403  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47404 .x-view-drag-insert-above {
47405         border-top:1px dotted #3366cc;
47406 }
47407 .x-view-drag-insert-below {
47408         border-bottom:1px dotted #3366cc;
47409 }
47410 </code></pre>
47411  * 
47412  */
47413  
47414 Roo.DDView = function(container, tpl, config) {
47415     Roo.DDView.superclass.constructor.apply(this, arguments);
47416     this.getEl().setStyle("outline", "0px none");
47417     this.getEl().unselectable();
47418     if (this.dragGroup) {
47419                 this.setDraggable(this.dragGroup.split(","));
47420     }
47421     if (this.dropGroup) {
47422                 this.setDroppable(this.dropGroup.split(","));
47423     }
47424     if (this.deletable) {
47425         this.setDeletable();
47426     }
47427     this.isDirtyFlag = false;
47428         this.addEvents({
47429                 "drop" : true
47430         });
47431 };
47432
47433 Roo.extend(Roo.DDView, Roo.View, {
47434 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47435 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47436 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47437 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47438
47439         isFormField: true,
47440
47441         reset: Roo.emptyFn,
47442         
47443         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47444
47445         validate: function() {
47446                 return true;
47447         },
47448         
47449         destroy: function() {
47450                 this.purgeListeners();
47451                 this.getEl.removeAllListeners();
47452                 this.getEl().remove();
47453                 if (this.dragZone) {
47454                         if (this.dragZone.destroy) {
47455                                 this.dragZone.destroy();
47456                         }
47457                 }
47458                 if (this.dropZone) {
47459                         if (this.dropZone.destroy) {
47460                                 this.dropZone.destroy();
47461                         }
47462                 }
47463         },
47464
47465 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47466         getName: function() {
47467                 return this.name;
47468         },
47469
47470 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47471         setValue: function(v) {
47472                 if (!this.store) {
47473                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47474                 }
47475                 var data = {};
47476                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47477                 this.store.proxy = new Roo.data.MemoryProxy(data);
47478                 this.store.load();
47479         },
47480
47481 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47482         getValue: function() {
47483                 var result = '(';
47484                 this.store.each(function(rec) {
47485                         result += rec.id + ',';
47486                 });
47487                 return result.substr(0, result.length - 1) + ')';
47488         },
47489         
47490         getIds: function() {
47491                 var i = 0, result = new Array(this.store.getCount());
47492                 this.store.each(function(rec) {
47493                         result[i++] = rec.id;
47494                 });
47495                 return result;
47496         },
47497         
47498         isDirty: function() {
47499                 return this.isDirtyFlag;
47500         },
47501
47502 /**
47503  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47504  *      whole Element becomes the target, and this causes the drop gesture to append.
47505  */
47506     getTargetFromEvent : function(e) {
47507                 var target = e.getTarget();
47508                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47509                 target = target.parentNode;
47510                 }
47511                 if (!target) {
47512                         target = this.el.dom.lastChild || this.el.dom;
47513                 }
47514                 return target;
47515     },
47516
47517 /**
47518  *      Create the drag data which consists of an object which has the property "ddel" as
47519  *      the drag proxy element. 
47520  */
47521     getDragData : function(e) {
47522         var target = this.findItemFromChild(e.getTarget());
47523                 if(target) {
47524                         this.handleSelection(e);
47525                         var selNodes = this.getSelectedNodes();
47526             var dragData = {
47527                 source: this,
47528                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47529                 nodes: selNodes,
47530                 records: []
47531                         };
47532                         var selectedIndices = this.getSelectedIndexes();
47533                         for (var i = 0; i < selectedIndices.length; i++) {
47534                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47535                         }
47536                         if (selNodes.length == 1) {
47537                                 dragData.ddel = target.cloneNode(true); // the div element
47538                         } else {
47539                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47540                                 div.className = 'multi-proxy';
47541                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47542                                         div.appendChild(selNodes[i].cloneNode(true));
47543                                 }
47544                                 dragData.ddel = div;
47545                         }
47546             //console.log(dragData)
47547             //console.log(dragData.ddel.innerHTML)
47548                         return dragData;
47549                 }
47550         //console.log('nodragData')
47551                 return false;
47552     },
47553     
47554 /**     Specify to which ddGroup items in this DDView may be dragged. */
47555     setDraggable: function(ddGroup) {
47556         if (ddGroup instanceof Array) {
47557                 Roo.each(ddGroup, this.setDraggable, this);
47558                 return;
47559         }
47560         if (this.dragZone) {
47561                 this.dragZone.addToGroup(ddGroup);
47562         } else {
47563                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47564                                 containerScroll: true,
47565                                 ddGroup: ddGroup 
47566
47567                         });
47568 //                      Draggability implies selection. DragZone's mousedown selects the element.
47569                         if (!this.multiSelect) { this.singleSelect = true; }
47570
47571 //                      Wire the DragZone's handlers up to methods in *this*
47572                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47573                 }
47574     },
47575
47576 /**     Specify from which ddGroup this DDView accepts drops. */
47577     setDroppable: function(ddGroup) {
47578         if (ddGroup instanceof Array) {
47579                 Roo.each(ddGroup, this.setDroppable, this);
47580                 return;
47581         }
47582         if (this.dropZone) {
47583                 this.dropZone.addToGroup(ddGroup);
47584         } else {
47585                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47586                                 containerScroll: true,
47587                                 ddGroup: ddGroup
47588                         });
47589
47590 //                      Wire the DropZone's handlers up to methods in *this*
47591                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47592                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47593                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47594                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47595                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47596                 }
47597     },
47598
47599 /**     Decide whether to drop above or below a View node. */
47600     getDropPoint : function(e, n, dd){
47601         if (n == this.el.dom) { return "above"; }
47602                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47603                 var c = t + (b - t) / 2;
47604                 var y = Roo.lib.Event.getPageY(e);
47605                 if(y <= c) {
47606                         return "above";
47607                 }else{
47608                         return "below";
47609                 }
47610     },
47611
47612     onNodeEnter : function(n, dd, e, data){
47613                 return false;
47614     },
47615     
47616     onNodeOver : function(n, dd, e, data){
47617                 var pt = this.getDropPoint(e, n, dd);
47618                 // set the insert point style on the target node
47619                 var dragElClass = this.dropNotAllowed;
47620                 if (pt) {
47621                         var targetElClass;
47622                         if (pt == "above"){
47623                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47624                                 targetElClass = "x-view-drag-insert-above";
47625                         } else {
47626                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47627                                 targetElClass = "x-view-drag-insert-below";
47628                         }
47629                         if (this.lastInsertClass != targetElClass){
47630                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47631                                 this.lastInsertClass = targetElClass;
47632                         }
47633                 }
47634                 return dragElClass;
47635         },
47636
47637     onNodeOut : function(n, dd, e, data){
47638                 this.removeDropIndicators(n);
47639     },
47640
47641     onNodeDrop : function(n, dd, e, data){
47642         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47643                 return false;
47644         }
47645         var pt = this.getDropPoint(e, n, dd);
47646                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47647                 if (pt == "below") { insertAt++; }
47648                 for (var i = 0; i < data.records.length; i++) {
47649                         var r = data.records[i];
47650                         var dup = this.store.getById(r.id);
47651                         if (dup && (dd != this.dragZone)) {
47652                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
47653                         } else {
47654                                 if (data.copy) {
47655                                         this.store.insert(insertAt++, r.copy());
47656                                 } else {
47657                                         data.source.isDirtyFlag = true;
47658                                         r.store.remove(r);
47659                                         this.store.insert(insertAt++, r);
47660                                 }
47661                                 this.isDirtyFlag = true;
47662                         }
47663                 }
47664                 this.dragZone.cachedTarget = null;
47665                 return true;
47666     },
47667
47668     removeDropIndicators : function(n){
47669                 if(n){
47670                         Roo.fly(n).removeClass([
47671                                 "x-view-drag-insert-above",
47672                                 "x-view-drag-insert-below"]);
47673                         this.lastInsertClass = "_noclass";
47674                 }
47675     },
47676
47677 /**
47678  *      Utility method. Add a delete option to the DDView's context menu.
47679  *      @param {String} imageUrl The URL of the "delete" icon image.
47680  */
47681         setDeletable: function(imageUrl) {
47682                 if (!this.singleSelect && !this.multiSelect) {
47683                         this.singleSelect = true;
47684                 }
47685                 var c = this.getContextMenu();
47686                 this.contextMenu.on("itemclick", function(item) {
47687                         switch (item.id) {
47688                                 case "delete":
47689                                         this.remove(this.getSelectedIndexes());
47690                                         break;
47691                         }
47692                 }, this);
47693                 this.contextMenu.add({
47694                         icon: imageUrl,
47695                         id: "delete",
47696                         text: 'Delete'
47697                 });
47698         },
47699         
47700 /**     Return the context menu for this DDView. */
47701         getContextMenu: function() {
47702                 if (!this.contextMenu) {
47703 //                      Create the View's context menu
47704                         this.contextMenu = new Roo.menu.Menu({
47705                                 id: this.id + "-contextmenu"
47706                         });
47707                         this.el.on("contextmenu", this.showContextMenu, this);
47708                 }
47709                 return this.contextMenu;
47710         },
47711         
47712         disableContextMenu: function() {
47713                 if (this.contextMenu) {
47714                         this.el.un("contextmenu", this.showContextMenu, this);
47715                 }
47716         },
47717
47718         showContextMenu: function(e, item) {
47719         item = this.findItemFromChild(e.getTarget());
47720                 if (item) {
47721                         e.stopEvent();
47722                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
47723                         this.contextMenu.showAt(e.getXY());
47724             }
47725     },
47726
47727 /**
47728  *      Remove {@link Roo.data.Record}s at the specified indices.
47729  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
47730  */
47731     remove: function(selectedIndices) {
47732                 selectedIndices = [].concat(selectedIndices);
47733                 for (var i = 0; i < selectedIndices.length; i++) {
47734                         var rec = this.store.getAt(selectedIndices[i]);
47735                         this.store.remove(rec);
47736                 }
47737     },
47738
47739 /**
47740  *      Double click fires the event, but also, if this is draggable, and there is only one other
47741  *      related DropZone, it transfers the selected node.
47742  */
47743     onDblClick : function(e){
47744         var item = this.findItemFromChild(e.getTarget());
47745         if(item){
47746             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
47747                 return false;
47748             }
47749             if (this.dragGroup) {
47750                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
47751                     while (targets.indexOf(this.dropZone) > -1) {
47752                             targets.remove(this.dropZone);
47753                                 }
47754                     if (targets.length == 1) {
47755                                         this.dragZone.cachedTarget = null;
47756                         var el = Roo.get(targets[0].getEl());
47757                         var box = el.getBox(true);
47758                         targets[0].onNodeDrop(el.dom, {
47759                                 target: el.dom,
47760                                 xy: [box.x, box.y + box.height - 1]
47761                         }, null, this.getDragData(e));
47762                     }
47763                 }
47764         }
47765     },
47766     
47767     handleSelection: function(e) {
47768                 this.dragZone.cachedTarget = null;
47769         var item = this.findItemFromChild(e.getTarget());
47770         if (!item) {
47771                 this.clearSelections(true);
47772                 return;
47773         }
47774                 if (item && (this.multiSelect || this.singleSelect)){
47775                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
47776                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
47777                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
47778                                 this.unselect(item);
47779                         } else {
47780                                 this.select(item, this.multiSelect && e.ctrlKey);
47781                                 this.lastSelection = item;
47782                         }
47783                 }
47784     },
47785
47786     onItemClick : function(item, index, e){
47787                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
47788                         return false;
47789                 }
47790                 return true;
47791     },
47792
47793     unselect : function(nodeInfo, suppressEvent){
47794                 var node = this.getNode(nodeInfo);
47795                 if(node && this.isSelected(node)){
47796                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
47797                                 Roo.fly(node).removeClass(this.selectedClass);
47798                                 this.selections.remove(node);
47799                                 if(!suppressEvent){
47800                                         this.fireEvent("selectionchange", this, this.selections);
47801                                 }
47802                         }
47803                 }
47804     }
47805 });
47806 /*
47807  * Based on:
47808  * Ext JS Library 1.1.1
47809  * Copyright(c) 2006-2007, Ext JS, LLC.
47810  *
47811  * Originally Released Under LGPL - original licence link has changed is not relivant.
47812  *
47813  * Fork - LGPL
47814  * <script type="text/javascript">
47815  */
47816  
47817 /**
47818  * @class Roo.LayoutManager
47819  * @extends Roo.util.Observable
47820  * Base class for layout managers.
47821  */
47822 Roo.LayoutManager = function(container, config){
47823     Roo.LayoutManager.superclass.constructor.call(this);
47824     this.el = Roo.get(container);
47825     // ie scrollbar fix
47826     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
47827         document.body.scroll = "no";
47828     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
47829         this.el.position('relative');
47830     }
47831     this.id = this.el.id;
47832     this.el.addClass("x-layout-container");
47833     /** false to disable window resize monitoring @type Boolean */
47834     this.monitorWindowResize = true;
47835     this.regions = {};
47836     this.addEvents({
47837         /**
47838          * @event layout
47839          * Fires when a layout is performed. 
47840          * @param {Roo.LayoutManager} this
47841          */
47842         "layout" : true,
47843         /**
47844          * @event regionresized
47845          * Fires when the user resizes a region. 
47846          * @param {Roo.LayoutRegion} region The resized region
47847          * @param {Number} newSize The new size (width for east/west, height for north/south)
47848          */
47849         "regionresized" : true,
47850         /**
47851          * @event regioncollapsed
47852          * Fires when a region is collapsed. 
47853          * @param {Roo.LayoutRegion} region The collapsed region
47854          */
47855         "regioncollapsed" : true,
47856         /**
47857          * @event regionexpanded
47858          * Fires when a region is expanded.  
47859          * @param {Roo.LayoutRegion} region The expanded region
47860          */
47861         "regionexpanded" : true
47862     });
47863     this.updating = false;
47864     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47865 };
47866
47867 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
47868     /**
47869      * Returns true if this layout is currently being updated
47870      * @return {Boolean}
47871      */
47872     isUpdating : function(){
47873         return this.updating; 
47874     },
47875     
47876     /**
47877      * Suspend the LayoutManager from doing auto-layouts while
47878      * making multiple add or remove calls
47879      */
47880     beginUpdate : function(){
47881         this.updating = true;    
47882     },
47883     
47884     /**
47885      * Restore auto-layouts and optionally disable the manager from performing a layout
47886      * @param {Boolean} noLayout true to disable a layout update 
47887      */
47888     endUpdate : function(noLayout){
47889         this.updating = false;
47890         if(!noLayout){
47891             this.layout();
47892         }    
47893     },
47894     
47895     layout: function(){
47896         
47897     },
47898     
47899     onRegionResized : function(region, newSize){
47900         this.fireEvent("regionresized", region, newSize);
47901         this.layout();
47902     },
47903     
47904     onRegionCollapsed : function(region){
47905         this.fireEvent("regioncollapsed", region);
47906     },
47907     
47908     onRegionExpanded : function(region){
47909         this.fireEvent("regionexpanded", region);
47910     },
47911         
47912     /**
47913      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
47914      * performs box-model adjustments.
47915      * @return {Object} The size as an object {width: (the width), height: (the height)}
47916      */
47917     getViewSize : function(){
47918         var size;
47919         if(this.el.dom != document.body){
47920             size = this.el.getSize();
47921         }else{
47922             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
47923         }
47924         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
47925         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
47926         return size;
47927     },
47928     
47929     /**
47930      * Returns the Element this layout is bound to.
47931      * @return {Roo.Element}
47932      */
47933     getEl : function(){
47934         return this.el;
47935     },
47936     
47937     /**
47938      * Returns the specified region.
47939      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
47940      * @return {Roo.LayoutRegion}
47941      */
47942     getRegion : function(target){
47943         return this.regions[target.toLowerCase()];
47944     },
47945     
47946     onWindowResize : function(){
47947         if(this.monitorWindowResize){
47948             this.layout();
47949         }
47950     }
47951 });/*
47952  * Based on:
47953  * Ext JS Library 1.1.1
47954  * Copyright(c) 2006-2007, Ext JS, LLC.
47955  *
47956  * Originally Released Under LGPL - original licence link has changed is not relivant.
47957  *
47958  * Fork - LGPL
47959  * <script type="text/javascript">
47960  */
47961 /**
47962  * @class Roo.BorderLayout
47963  * @extends Roo.LayoutManager
47964  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
47965  * please see: <br><br>
47966  * <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>
47967  * <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>
47968  * Example:
47969  <pre><code>
47970  var layout = new Roo.BorderLayout(document.body, {
47971     north: {
47972         initialSize: 25,
47973         titlebar: false
47974     },
47975     west: {
47976         split:true,
47977         initialSize: 200,
47978         minSize: 175,
47979         maxSize: 400,
47980         titlebar: true,
47981         collapsible: true
47982     },
47983     east: {
47984         split:true,
47985         initialSize: 202,
47986         minSize: 175,
47987         maxSize: 400,
47988         titlebar: true,
47989         collapsible: true
47990     },
47991     south: {
47992         split:true,
47993         initialSize: 100,
47994         minSize: 100,
47995         maxSize: 200,
47996         titlebar: true,
47997         collapsible: true
47998     },
47999     center: {
48000         titlebar: true,
48001         autoScroll:true,
48002         resizeTabs: true,
48003         minTabWidth: 50,
48004         preferredTabWidth: 150
48005     }
48006 });
48007
48008 // shorthand
48009 var CP = Roo.ContentPanel;
48010
48011 layout.beginUpdate();
48012 layout.add("north", new CP("north", "North"));
48013 layout.add("south", new CP("south", {title: "South", closable: true}));
48014 layout.add("west", new CP("west", {title: "West"}));
48015 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48016 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48017 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48018 layout.getRegion("center").showPanel("center1");
48019 layout.endUpdate();
48020 </code></pre>
48021
48022 <b>The container the layout is rendered into can be either the body element or any other element.
48023 If it is not the body element, the container needs to either be an absolute positioned element,
48024 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48025 the container size if it is not the body element.</b>
48026
48027 * @constructor
48028 * Create a new BorderLayout
48029 * @param {String/HTMLElement/Element} container The container this layout is bound to
48030 * @param {Object} config Configuration options
48031  */
48032 Roo.BorderLayout = function(container, config){
48033     config = config || {};
48034     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48035     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48036     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48037         var target = this.factory.validRegions[i];
48038         if(config[target]){
48039             this.addRegion(target, config[target]);
48040         }
48041     }
48042 };
48043
48044 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48045     /**
48046      * Creates and adds a new region if it doesn't already exist.
48047      * @param {String} target The target region key (north, south, east, west or center).
48048      * @param {Object} config The regions config object
48049      * @return {BorderLayoutRegion} The new region
48050      */
48051     addRegion : function(target, config){
48052         if(!this.regions[target]){
48053             var r = this.factory.create(target, this, config);
48054             this.bindRegion(target, r);
48055         }
48056         return this.regions[target];
48057     },
48058
48059     // private (kinda)
48060     bindRegion : function(name, r){
48061         this.regions[name] = r;
48062         r.on("visibilitychange", this.layout, this);
48063         r.on("paneladded", this.layout, this);
48064         r.on("panelremoved", this.layout, this);
48065         r.on("invalidated", this.layout, this);
48066         r.on("resized", this.onRegionResized, this);
48067         r.on("collapsed", this.onRegionCollapsed, this);
48068         r.on("expanded", this.onRegionExpanded, this);
48069     },
48070
48071     /**
48072      * Performs a layout update.
48073      */
48074     layout : function(){
48075         if(this.updating) return;
48076         var size = this.getViewSize();
48077         var w = size.width;
48078         var h = size.height;
48079         var centerW = w;
48080         var centerH = h;
48081         var centerY = 0;
48082         var centerX = 0;
48083         //var x = 0, y = 0;
48084
48085         var rs = this.regions;
48086         var north = rs["north"];
48087         var south = rs["south"]; 
48088         var west = rs["west"];
48089         var east = rs["east"];
48090         var center = rs["center"];
48091         //if(this.hideOnLayout){ // not supported anymore
48092             //c.el.setStyle("display", "none");
48093         //}
48094         if(north && north.isVisible()){
48095             var b = north.getBox();
48096             var m = north.getMargins();
48097             b.width = w - (m.left+m.right);
48098             b.x = m.left;
48099             b.y = m.top;
48100             centerY = b.height + b.y + m.bottom;
48101             centerH -= centerY;
48102             north.updateBox(this.safeBox(b));
48103         }
48104         if(south && south.isVisible()){
48105             var b = south.getBox();
48106             var m = south.getMargins();
48107             b.width = w - (m.left+m.right);
48108             b.x = m.left;
48109             var totalHeight = (b.height + m.top + m.bottom);
48110             b.y = h - totalHeight + m.top;
48111             centerH -= totalHeight;
48112             south.updateBox(this.safeBox(b));
48113         }
48114         if(west && west.isVisible()){
48115             var b = west.getBox();
48116             var m = west.getMargins();
48117             b.height = centerH - (m.top+m.bottom);
48118             b.x = m.left;
48119             b.y = centerY + m.top;
48120             var totalWidth = (b.width + m.left + m.right);
48121             centerX += totalWidth;
48122             centerW -= totalWidth;
48123             west.updateBox(this.safeBox(b));
48124         }
48125         if(east && east.isVisible()){
48126             var b = east.getBox();
48127             var m = east.getMargins();
48128             b.height = centerH - (m.top+m.bottom);
48129             var totalWidth = (b.width + m.left + m.right);
48130             b.x = w - totalWidth + m.left;
48131             b.y = centerY + m.top;
48132             centerW -= totalWidth;
48133             east.updateBox(this.safeBox(b));
48134         }
48135         if(center){
48136             var m = center.getMargins();
48137             var centerBox = {
48138                 x: centerX + m.left,
48139                 y: centerY + m.top,
48140                 width: centerW - (m.left+m.right),
48141                 height: centerH - (m.top+m.bottom)
48142             };
48143             //if(this.hideOnLayout){
48144                 //center.el.setStyle("display", "block");
48145             //}
48146             center.updateBox(this.safeBox(centerBox));
48147         }
48148         this.el.repaint();
48149         this.fireEvent("layout", this);
48150     },
48151
48152     // private
48153     safeBox : function(box){
48154         box.width = Math.max(0, box.width);
48155         box.height = Math.max(0, box.height);
48156         return box;
48157     },
48158
48159     /**
48160      * Adds a ContentPanel (or subclass) to this layout.
48161      * @param {String} target The target region key (north, south, east, west or center).
48162      * @param {Roo.ContentPanel} panel The panel to add
48163      * @return {Roo.ContentPanel} The added panel
48164      */
48165     add : function(target, panel){
48166          
48167         target = target.toLowerCase();
48168         return this.regions[target].add(panel);
48169     },
48170
48171     /**
48172      * Remove a ContentPanel (or subclass) to this layout.
48173      * @param {String} target The target region key (north, south, east, west or center).
48174      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48175      * @return {Roo.ContentPanel} The removed panel
48176      */
48177     remove : function(target, panel){
48178         target = target.toLowerCase();
48179         return this.regions[target].remove(panel);
48180     },
48181
48182     /**
48183      * Searches all regions for a panel with the specified id
48184      * @param {String} panelId
48185      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48186      */
48187     findPanel : function(panelId){
48188         var rs = this.regions;
48189         for(var target in rs){
48190             if(typeof rs[target] != "function"){
48191                 var p = rs[target].getPanel(panelId);
48192                 if(p){
48193                     return p;
48194                 }
48195             }
48196         }
48197         return null;
48198     },
48199
48200     /**
48201      * Searches all regions for a panel with the specified id and activates (shows) it.
48202      * @param {String/ContentPanel} panelId The panels id or the panel itself
48203      * @return {Roo.ContentPanel} The shown panel or null
48204      */
48205     showPanel : function(panelId) {
48206       var rs = this.regions;
48207       for(var target in rs){
48208          var r = rs[target];
48209          if(typeof r != "function"){
48210             if(r.hasPanel(panelId)){
48211                return r.showPanel(panelId);
48212             }
48213          }
48214       }
48215       return null;
48216    },
48217
48218    /**
48219      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48220      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48221      */
48222     restoreState : function(provider){
48223         if(!provider){
48224             provider = Roo.state.Manager;
48225         }
48226         var sm = new Roo.LayoutStateManager();
48227         sm.init(this, provider);
48228     },
48229
48230     /**
48231      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48232      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48233      * a valid ContentPanel config object.  Example:
48234      * <pre><code>
48235 // Create the main layout
48236 var layout = new Roo.BorderLayout('main-ct', {
48237     west: {
48238         split:true,
48239         minSize: 175,
48240         titlebar: true
48241     },
48242     center: {
48243         title:'Components'
48244     }
48245 }, 'main-ct');
48246
48247 // Create and add multiple ContentPanels at once via configs
48248 layout.batchAdd({
48249    west: {
48250        id: 'source-files',
48251        autoCreate:true,
48252        title:'Ext Source Files',
48253        autoScroll:true,
48254        fitToFrame:true
48255    },
48256    center : {
48257        el: cview,
48258        autoScroll:true,
48259        fitToFrame:true,
48260        toolbar: tb,
48261        resizeEl:'cbody'
48262    }
48263 });
48264 </code></pre>
48265      * @param {Object} regions An object containing ContentPanel configs by region name
48266      */
48267     batchAdd : function(regions){
48268         this.beginUpdate();
48269         for(var rname in regions){
48270             var lr = this.regions[rname];
48271             if(lr){
48272                 this.addTypedPanels(lr, regions[rname]);
48273             }
48274         }
48275         this.endUpdate();
48276     },
48277
48278     // private
48279     addTypedPanels : function(lr, ps){
48280         if(typeof ps == 'string'){
48281             lr.add(new Roo.ContentPanel(ps));
48282         }
48283         else if(ps instanceof Array){
48284             for(var i =0, len = ps.length; i < len; i++){
48285                 this.addTypedPanels(lr, ps[i]);
48286             }
48287         }
48288         else if(!ps.events){ // raw config?
48289             var el = ps.el;
48290             delete ps.el; // prevent conflict
48291             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48292         }
48293         else {  // panel object assumed!
48294             lr.add(ps);
48295         }
48296     },
48297     /**
48298      * Adds a xtype elements to the layout.
48299      * <pre><code>
48300
48301 layout.addxtype({
48302        xtype : 'ContentPanel',
48303        region: 'west',
48304        items: [ .... ]
48305    }
48306 );
48307
48308 layout.addxtype({
48309         xtype : 'NestedLayoutPanel',
48310         region: 'west',
48311         layout: {
48312            center: { },
48313            west: { }   
48314         },
48315         items : [ ... list of content panels or nested layout panels.. ]
48316    }
48317 );
48318 </code></pre>
48319      * @param {Object} cfg Xtype definition of item to add.
48320      */
48321     addxtype : function(cfg)
48322     {
48323         // basically accepts a pannel...
48324         // can accept a layout region..!?!?
48325         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48326         
48327         if (!cfg.xtype.match(/Panel$/)) {
48328             return false;
48329         }
48330         var ret = false;
48331         
48332         if (typeof(cfg.region) == 'undefined') {
48333             Roo.log("Failed to add Panel, region was not set");
48334             Roo.log(cfg);
48335             return false;
48336         }
48337         var region = cfg.region;
48338         delete cfg.region;
48339         
48340           
48341         var xitems = [];
48342         if (cfg.items) {
48343             xitems = cfg.items;
48344             delete cfg.items;
48345         }
48346         var nb = false;
48347         
48348         switch(cfg.xtype) 
48349         {
48350             case 'ContentPanel':  // ContentPanel (el, cfg)
48351             case 'ScrollPanel':  // ContentPanel (el, cfg)
48352             case 'ViewPanel': 
48353                 if(cfg.autoCreate) {
48354                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48355                 } else {
48356                     var el = this.el.createChild();
48357                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48358                 }
48359                 
48360                 this.add(region, ret);
48361                 break;
48362             
48363             
48364             case 'TreePanel': // our new panel!
48365                 cfg.el = this.el.createChild();
48366                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48367                 this.add(region, ret);
48368                 break;
48369             
48370             case 'NestedLayoutPanel': 
48371                 // create a new Layout (which is  a Border Layout...
48372                 var el = this.el.createChild();
48373                 var clayout = cfg.layout;
48374                 delete cfg.layout;
48375                 clayout.items   = clayout.items  || [];
48376                 // replace this exitems with the clayout ones..
48377                 xitems = clayout.items;
48378                  
48379                 
48380                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48381                     cfg.background = false;
48382                 }
48383                 var layout = new Roo.BorderLayout(el, clayout);
48384                 
48385                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48386                 //console.log('adding nested layout panel '  + cfg.toSource());
48387                 this.add(region, ret);
48388                 nb = {}; /// find first...
48389                 break;
48390                 
48391             case 'GridPanel': 
48392             
48393                 // needs grid and region
48394                 
48395                 //var el = this.getRegion(region).el.createChild();
48396                 var el = this.el.createChild();
48397                 // create the grid first...
48398                 
48399                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48400                 delete cfg.grid;
48401                 if (region == 'center' && this.active ) {
48402                     cfg.background = false;
48403                 }
48404                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48405                 
48406                 this.add(region, ret);
48407                 if (cfg.background) {
48408                     ret.on('activate', function(gp) {
48409                         if (!gp.grid.rendered) {
48410                             gp.grid.render();
48411                         }
48412                     });
48413                 } else {
48414                     grid.render();
48415                 }
48416                 break;
48417            
48418            
48419            
48420                 
48421                 
48422                 
48423             default:
48424                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
48425                     
48426                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48427                     this.add(region, ret);
48428                 } else {
48429                 
48430                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48431                     return null;
48432                 }
48433                 
48434              // GridPanel (grid, cfg)
48435             
48436         }
48437         this.beginUpdate();
48438         // add children..
48439         var region = '';
48440         var abn = {};
48441         Roo.each(xitems, function(i)  {
48442             region = nb && i.region ? i.region : false;
48443             
48444             var add = ret.addxtype(i);
48445            
48446             if (region) {
48447                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48448                 if (!i.background) {
48449                     abn[region] = nb[region] ;
48450                 }
48451             }
48452             
48453         });
48454         this.endUpdate();
48455
48456         // make the last non-background panel active..
48457         //if (nb) { Roo.log(abn); }
48458         if (nb) {
48459             
48460             for(var r in abn) {
48461                 region = this.getRegion(r);
48462                 if (region) {
48463                     // tried using nb[r], but it does not work..
48464                      
48465                     region.showPanel(abn[r]);
48466                    
48467                 }
48468             }
48469         }
48470         return ret;
48471         
48472     }
48473 });
48474
48475 /**
48476  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48477  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48478  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48479  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48480  * <pre><code>
48481 // shorthand
48482 var CP = Roo.ContentPanel;
48483
48484 var layout = Roo.BorderLayout.create({
48485     north: {
48486         initialSize: 25,
48487         titlebar: false,
48488         panels: [new CP("north", "North")]
48489     },
48490     west: {
48491         split:true,
48492         initialSize: 200,
48493         minSize: 175,
48494         maxSize: 400,
48495         titlebar: true,
48496         collapsible: true,
48497         panels: [new CP("west", {title: "West"})]
48498     },
48499     east: {
48500         split:true,
48501         initialSize: 202,
48502         minSize: 175,
48503         maxSize: 400,
48504         titlebar: true,
48505         collapsible: true,
48506         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48507     },
48508     south: {
48509         split:true,
48510         initialSize: 100,
48511         minSize: 100,
48512         maxSize: 200,
48513         titlebar: true,
48514         collapsible: true,
48515         panels: [new CP("south", {title: "South", closable: true})]
48516     },
48517     center: {
48518         titlebar: true,
48519         autoScroll:true,
48520         resizeTabs: true,
48521         minTabWidth: 50,
48522         preferredTabWidth: 150,
48523         panels: [
48524             new CP("center1", {title: "Close Me", closable: true}),
48525             new CP("center2", {title: "Center Panel", closable: false})
48526         ]
48527     }
48528 }, document.body);
48529
48530 layout.getRegion("center").showPanel("center1");
48531 </code></pre>
48532  * @param config
48533  * @param targetEl
48534  */
48535 Roo.BorderLayout.create = function(config, targetEl){
48536     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48537     layout.beginUpdate();
48538     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48539     for(var j = 0, jlen = regions.length; j < jlen; j++){
48540         var lr = regions[j];
48541         if(layout.regions[lr] && config[lr].panels){
48542             var r = layout.regions[lr];
48543             var ps = config[lr].panels;
48544             layout.addTypedPanels(r, ps);
48545         }
48546     }
48547     layout.endUpdate();
48548     return layout;
48549 };
48550
48551 // private
48552 Roo.BorderLayout.RegionFactory = {
48553     // private
48554     validRegions : ["north","south","east","west","center"],
48555
48556     // private
48557     create : function(target, mgr, config){
48558         target = target.toLowerCase();
48559         if(config.lightweight || config.basic){
48560             return new Roo.BasicLayoutRegion(mgr, config, target);
48561         }
48562         switch(target){
48563             case "north":
48564                 return new Roo.NorthLayoutRegion(mgr, config);
48565             case "south":
48566                 return new Roo.SouthLayoutRegion(mgr, config);
48567             case "east":
48568                 return new Roo.EastLayoutRegion(mgr, config);
48569             case "west":
48570                 return new Roo.WestLayoutRegion(mgr, config);
48571             case "center":
48572                 return new Roo.CenterLayoutRegion(mgr, config);
48573         }
48574         throw 'Layout region "'+target+'" not supported.';
48575     }
48576 };/*
48577  * Based on:
48578  * Ext JS Library 1.1.1
48579  * Copyright(c) 2006-2007, Ext JS, LLC.
48580  *
48581  * Originally Released Under LGPL - original licence link has changed is not relivant.
48582  *
48583  * Fork - LGPL
48584  * <script type="text/javascript">
48585  */
48586  
48587 /**
48588  * @class Roo.BasicLayoutRegion
48589  * @extends Roo.util.Observable
48590  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48591  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48592  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48593  */
48594 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48595     this.mgr = mgr;
48596     this.position  = pos;
48597     this.events = {
48598         /**
48599          * @scope Roo.BasicLayoutRegion
48600          */
48601         
48602         /**
48603          * @event beforeremove
48604          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48605          * @param {Roo.LayoutRegion} this
48606          * @param {Roo.ContentPanel} panel The panel
48607          * @param {Object} e The cancel event object
48608          */
48609         "beforeremove" : true,
48610         /**
48611          * @event invalidated
48612          * Fires when the layout for this region is changed.
48613          * @param {Roo.LayoutRegion} this
48614          */
48615         "invalidated" : true,
48616         /**
48617          * @event visibilitychange
48618          * Fires when this region is shown or hidden 
48619          * @param {Roo.LayoutRegion} this
48620          * @param {Boolean} visibility true or false
48621          */
48622         "visibilitychange" : true,
48623         /**
48624          * @event paneladded
48625          * Fires when a panel is added. 
48626          * @param {Roo.LayoutRegion} this
48627          * @param {Roo.ContentPanel} panel The panel
48628          */
48629         "paneladded" : true,
48630         /**
48631          * @event panelremoved
48632          * Fires when a panel is removed. 
48633          * @param {Roo.LayoutRegion} this
48634          * @param {Roo.ContentPanel} panel The panel
48635          */
48636         "panelremoved" : true,
48637         /**
48638          * @event collapsed
48639          * Fires when this region is collapsed.
48640          * @param {Roo.LayoutRegion} this
48641          */
48642         "collapsed" : true,
48643         /**
48644          * @event expanded
48645          * Fires when this region is expanded.
48646          * @param {Roo.LayoutRegion} this
48647          */
48648         "expanded" : true,
48649         /**
48650          * @event slideshow
48651          * Fires when this region is slid into view.
48652          * @param {Roo.LayoutRegion} this
48653          */
48654         "slideshow" : true,
48655         /**
48656          * @event slidehide
48657          * Fires when this region slides out of view. 
48658          * @param {Roo.LayoutRegion} this
48659          */
48660         "slidehide" : true,
48661         /**
48662          * @event panelactivated
48663          * Fires when a panel is activated. 
48664          * @param {Roo.LayoutRegion} this
48665          * @param {Roo.ContentPanel} panel The activated panel
48666          */
48667         "panelactivated" : true,
48668         /**
48669          * @event resized
48670          * Fires when the user resizes this region. 
48671          * @param {Roo.LayoutRegion} this
48672          * @param {Number} newSize The new size (width for east/west, height for north/south)
48673          */
48674         "resized" : true
48675     };
48676     /** A collection of panels in this region. @type Roo.util.MixedCollection */
48677     this.panels = new Roo.util.MixedCollection();
48678     this.panels.getKey = this.getPanelId.createDelegate(this);
48679     this.box = null;
48680     this.activePanel = null;
48681     // ensure listeners are added...
48682     
48683     if (config.listeners || config.events) {
48684         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
48685             listeners : config.listeners || {},
48686             events : config.events || {}
48687         });
48688     }
48689     
48690     if(skipConfig !== true){
48691         this.applyConfig(config);
48692     }
48693 };
48694
48695 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
48696     getPanelId : function(p){
48697         return p.getId();
48698     },
48699     
48700     applyConfig : function(config){
48701         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
48702         this.config = config;
48703         
48704     },
48705     
48706     /**
48707      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
48708      * the width, for horizontal (north, south) the height.
48709      * @param {Number} newSize The new width or height
48710      */
48711     resizeTo : function(newSize){
48712         var el = this.el ? this.el :
48713                  (this.activePanel ? this.activePanel.getEl() : null);
48714         if(el){
48715             switch(this.position){
48716                 case "east":
48717                 case "west":
48718                     el.setWidth(newSize);
48719                     this.fireEvent("resized", this, newSize);
48720                 break;
48721                 case "north":
48722                 case "south":
48723                     el.setHeight(newSize);
48724                     this.fireEvent("resized", this, newSize);
48725                 break;                
48726             }
48727         }
48728     },
48729     
48730     getBox : function(){
48731         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
48732     },
48733     
48734     getMargins : function(){
48735         return this.margins;
48736     },
48737     
48738     updateBox : function(box){
48739         this.box = box;
48740         var el = this.activePanel.getEl();
48741         el.dom.style.left = box.x + "px";
48742         el.dom.style.top = box.y + "px";
48743         this.activePanel.setSize(box.width, box.height);
48744     },
48745     
48746     /**
48747      * Returns the container element for this region.
48748      * @return {Roo.Element}
48749      */
48750     getEl : function(){
48751         return this.activePanel;
48752     },
48753     
48754     /**
48755      * Returns true if this region is currently visible.
48756      * @return {Boolean}
48757      */
48758     isVisible : function(){
48759         return this.activePanel ? true : false;
48760     },
48761     
48762     setActivePanel : function(panel){
48763         panel = this.getPanel(panel);
48764         if(this.activePanel && this.activePanel != panel){
48765             this.activePanel.setActiveState(false);
48766             this.activePanel.getEl().setLeftTop(-10000,-10000);
48767         }
48768         this.activePanel = panel;
48769         panel.setActiveState(true);
48770         if(this.box){
48771             panel.setSize(this.box.width, this.box.height);
48772         }
48773         this.fireEvent("panelactivated", this, panel);
48774         this.fireEvent("invalidated");
48775     },
48776     
48777     /**
48778      * Show the specified panel.
48779      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
48780      * @return {Roo.ContentPanel} The shown panel or null
48781      */
48782     showPanel : function(panel){
48783         if(panel = this.getPanel(panel)){
48784             this.setActivePanel(panel);
48785         }
48786         return panel;
48787     },
48788     
48789     /**
48790      * Get the active panel for this region.
48791      * @return {Roo.ContentPanel} The active panel or null
48792      */
48793     getActivePanel : function(){
48794         return this.activePanel;
48795     },
48796     
48797     /**
48798      * Add the passed ContentPanel(s)
48799      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48800      * @return {Roo.ContentPanel} The panel added (if only one was added)
48801      */
48802     add : function(panel){
48803         if(arguments.length > 1){
48804             for(var i = 0, len = arguments.length; i < len; i++) {
48805                 this.add(arguments[i]);
48806             }
48807             return null;
48808         }
48809         if(this.hasPanel(panel)){
48810             this.showPanel(panel);
48811             return panel;
48812         }
48813         var el = panel.getEl();
48814         if(el.dom.parentNode != this.mgr.el.dom){
48815             this.mgr.el.dom.appendChild(el.dom);
48816         }
48817         if(panel.setRegion){
48818             panel.setRegion(this);
48819         }
48820         this.panels.add(panel);
48821         el.setStyle("position", "absolute");
48822         if(!panel.background){
48823             this.setActivePanel(panel);
48824             if(this.config.initialSize && this.panels.getCount()==1){
48825                 this.resizeTo(this.config.initialSize);
48826             }
48827         }
48828         this.fireEvent("paneladded", this, panel);
48829         return panel;
48830     },
48831     
48832     /**
48833      * Returns true if the panel is in this region.
48834      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48835      * @return {Boolean}
48836      */
48837     hasPanel : function(panel){
48838         if(typeof panel == "object"){ // must be panel obj
48839             panel = panel.getId();
48840         }
48841         return this.getPanel(panel) ? true : false;
48842     },
48843     
48844     /**
48845      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48846      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48847      * @param {Boolean} preservePanel Overrides the config preservePanel option
48848      * @return {Roo.ContentPanel} The panel that was removed
48849      */
48850     remove : function(panel, preservePanel){
48851         panel = this.getPanel(panel);
48852         if(!panel){
48853             return null;
48854         }
48855         var e = {};
48856         this.fireEvent("beforeremove", this, panel, e);
48857         if(e.cancel === true){
48858             return null;
48859         }
48860         var panelId = panel.getId();
48861         this.panels.removeKey(panelId);
48862         return panel;
48863     },
48864     
48865     /**
48866      * Returns the panel specified or null if it's not in this region.
48867      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
48868      * @return {Roo.ContentPanel}
48869      */
48870     getPanel : function(id){
48871         if(typeof id == "object"){ // must be panel obj
48872             return id;
48873         }
48874         return this.panels.get(id);
48875     },
48876     
48877     /**
48878      * Returns this regions position (north/south/east/west/center).
48879      * @return {String} 
48880      */
48881     getPosition: function(){
48882         return this.position;    
48883     }
48884 });/*
48885  * Based on:
48886  * Ext JS Library 1.1.1
48887  * Copyright(c) 2006-2007, Ext JS, LLC.
48888  *
48889  * Originally Released Under LGPL - original licence link has changed is not relivant.
48890  *
48891  * Fork - LGPL
48892  * <script type="text/javascript">
48893  */
48894  
48895 /**
48896  * @class Roo.LayoutRegion
48897  * @extends Roo.BasicLayoutRegion
48898  * This class represents a region in a layout manager.
48899  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
48900  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
48901  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
48902  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
48903  * @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})
48904  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
48905  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
48906  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
48907  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
48908  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
48909  * @cfg {String}    title           The title for the region (overrides panel titles)
48910  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
48911  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
48912  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
48913  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
48914  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
48915  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
48916  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
48917  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
48918  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
48919  * @cfg {Boolean}   showPin         True to show a pin button
48920  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
48921  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
48922  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
48923  * @cfg {Number}    width           For East/West panels
48924  * @cfg {Number}    height          For North/South panels
48925  * @cfg {Boolean}   split           To show the splitter
48926  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
48927  */
48928 Roo.LayoutRegion = function(mgr, config, pos){
48929     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
48930     var dh = Roo.DomHelper;
48931     /** This region's container element 
48932     * @type Roo.Element */
48933     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
48934     /** This region's title element 
48935     * @type Roo.Element */
48936
48937     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
48938         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
48939         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
48940     ]}, true);
48941     this.titleEl.enableDisplayMode();
48942     /** This region's title text element 
48943     * @type HTMLElement */
48944     this.titleTextEl = this.titleEl.dom.firstChild;
48945     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
48946     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
48947     this.closeBtn.enableDisplayMode();
48948     this.closeBtn.on("click", this.closeClicked, this);
48949     this.closeBtn.hide();
48950
48951     this.createBody(config);
48952     this.visible = true;
48953     this.collapsed = false;
48954
48955     if(config.hideWhenEmpty){
48956         this.hide();
48957         this.on("paneladded", this.validateVisibility, this);
48958         this.on("panelremoved", this.validateVisibility, this);
48959     }
48960     this.applyConfig(config);
48961 };
48962
48963 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
48964
48965     createBody : function(){
48966         /** This region's body element 
48967         * @type Roo.Element */
48968         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
48969     },
48970
48971     applyConfig : function(c){
48972         if(c.collapsible && this.position != "center" && !this.collapsedEl){
48973             var dh = Roo.DomHelper;
48974             if(c.titlebar !== false){
48975                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
48976                 this.collapseBtn.on("click", this.collapse, this);
48977                 this.collapseBtn.enableDisplayMode();
48978
48979                 if(c.showPin === true || this.showPin){
48980                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
48981                     this.stickBtn.enableDisplayMode();
48982                     this.stickBtn.on("click", this.expand, this);
48983                     this.stickBtn.hide();
48984                 }
48985             }
48986             /** This region's collapsed element
48987             * @type Roo.Element */
48988             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
48989                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
48990             ]}, true);
48991             if(c.floatable !== false){
48992                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
48993                this.collapsedEl.on("click", this.collapseClick, this);
48994             }
48995
48996             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
48997                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
48998                    id: "message", unselectable: "on", style:{"float":"left"}});
48999                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49000              }
49001             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49002             this.expandBtn.on("click", this.expand, this);
49003         }
49004         if(this.collapseBtn){
49005             this.collapseBtn.setVisible(c.collapsible == true);
49006         }
49007         this.cmargins = c.cmargins || this.cmargins ||
49008                          (this.position == "west" || this.position == "east" ?
49009                              {top: 0, left: 2, right:2, bottom: 0} :
49010                              {top: 2, left: 0, right:0, bottom: 2});
49011         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49012         this.bottomTabs = c.tabPosition != "top";
49013         this.autoScroll = c.autoScroll || false;
49014         if(this.autoScroll){
49015             this.bodyEl.setStyle("overflow", "auto");
49016         }else{
49017             this.bodyEl.setStyle("overflow", "hidden");
49018         }
49019         //if(c.titlebar !== false){
49020             if((!c.titlebar && !c.title) || c.titlebar === false){
49021                 this.titleEl.hide();
49022             }else{
49023                 this.titleEl.show();
49024                 if(c.title){
49025                     this.titleTextEl.innerHTML = c.title;
49026                 }
49027             }
49028         //}
49029         this.duration = c.duration || .30;
49030         this.slideDuration = c.slideDuration || .45;
49031         this.config = c;
49032         if(c.collapsed){
49033             this.collapse(true);
49034         }
49035         if(c.hidden){
49036             this.hide();
49037         }
49038     },
49039     /**
49040      * Returns true if this region is currently visible.
49041      * @return {Boolean}
49042      */
49043     isVisible : function(){
49044         return this.visible;
49045     },
49046
49047     /**
49048      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49049      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49050      */
49051     setCollapsedTitle : function(title){
49052         title = title || "&#160;";
49053         if(this.collapsedTitleTextEl){
49054             this.collapsedTitleTextEl.innerHTML = title;
49055         }
49056     },
49057
49058     getBox : function(){
49059         var b;
49060         if(!this.collapsed){
49061             b = this.el.getBox(false, true);
49062         }else{
49063             b = this.collapsedEl.getBox(false, true);
49064         }
49065         return b;
49066     },
49067
49068     getMargins : function(){
49069         return this.collapsed ? this.cmargins : this.margins;
49070     },
49071
49072     highlight : function(){
49073         this.el.addClass("x-layout-panel-dragover");
49074     },
49075
49076     unhighlight : function(){
49077         this.el.removeClass("x-layout-panel-dragover");
49078     },
49079
49080     updateBox : function(box){
49081         this.box = box;
49082         if(!this.collapsed){
49083             this.el.dom.style.left = box.x + "px";
49084             this.el.dom.style.top = box.y + "px";
49085             this.updateBody(box.width, box.height);
49086         }else{
49087             this.collapsedEl.dom.style.left = box.x + "px";
49088             this.collapsedEl.dom.style.top = box.y + "px";
49089             this.collapsedEl.setSize(box.width, box.height);
49090         }
49091         if(this.tabs){
49092             this.tabs.autoSizeTabs();
49093         }
49094     },
49095
49096     updateBody : function(w, h){
49097         if(w !== null){
49098             this.el.setWidth(w);
49099             w -= this.el.getBorderWidth("rl");
49100             if(this.config.adjustments){
49101                 w += this.config.adjustments[0];
49102             }
49103         }
49104         if(h !== null){
49105             this.el.setHeight(h);
49106             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49107             h -= this.el.getBorderWidth("tb");
49108             if(this.config.adjustments){
49109                 h += this.config.adjustments[1];
49110             }
49111             this.bodyEl.setHeight(h);
49112             if(this.tabs){
49113                 h = this.tabs.syncHeight(h);
49114             }
49115         }
49116         if(this.panelSize){
49117             w = w !== null ? w : this.panelSize.width;
49118             h = h !== null ? h : this.panelSize.height;
49119         }
49120         if(this.activePanel){
49121             var el = this.activePanel.getEl();
49122             w = w !== null ? w : el.getWidth();
49123             h = h !== null ? h : el.getHeight();
49124             this.panelSize = {width: w, height: h};
49125             this.activePanel.setSize(w, h);
49126         }
49127         if(Roo.isIE && this.tabs){
49128             this.tabs.el.repaint();
49129         }
49130     },
49131
49132     /**
49133      * Returns the container element for this region.
49134      * @return {Roo.Element}
49135      */
49136     getEl : function(){
49137         return this.el;
49138     },
49139
49140     /**
49141      * Hides this region.
49142      */
49143     hide : function(){
49144         if(!this.collapsed){
49145             this.el.dom.style.left = "-2000px";
49146             this.el.hide();
49147         }else{
49148             this.collapsedEl.dom.style.left = "-2000px";
49149             this.collapsedEl.hide();
49150         }
49151         this.visible = false;
49152         this.fireEvent("visibilitychange", this, false);
49153     },
49154
49155     /**
49156      * Shows this region if it was previously hidden.
49157      */
49158     show : function(){
49159         if(!this.collapsed){
49160             this.el.show();
49161         }else{
49162             this.collapsedEl.show();
49163         }
49164         this.visible = true;
49165         this.fireEvent("visibilitychange", this, true);
49166     },
49167
49168     closeClicked : function(){
49169         if(this.activePanel){
49170             this.remove(this.activePanel);
49171         }
49172     },
49173
49174     collapseClick : function(e){
49175         if(this.isSlid){
49176            e.stopPropagation();
49177            this.slideIn();
49178         }else{
49179            e.stopPropagation();
49180            this.slideOut();
49181         }
49182     },
49183
49184     /**
49185      * Collapses this region.
49186      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49187      */
49188     collapse : function(skipAnim){
49189         if(this.collapsed) return;
49190         this.collapsed = true;
49191         if(this.split){
49192             this.split.el.hide();
49193         }
49194         if(this.config.animate && skipAnim !== true){
49195             this.fireEvent("invalidated", this);
49196             this.animateCollapse();
49197         }else{
49198             this.el.setLocation(-20000,-20000);
49199             this.el.hide();
49200             this.collapsedEl.show();
49201             this.fireEvent("collapsed", this);
49202             this.fireEvent("invalidated", this);
49203         }
49204     },
49205
49206     animateCollapse : function(){
49207         // overridden
49208     },
49209
49210     /**
49211      * Expands this region if it was previously collapsed.
49212      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49213      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49214      */
49215     expand : function(e, skipAnim){
49216         if(e) e.stopPropagation();
49217         if(!this.collapsed || this.el.hasActiveFx()) return;
49218         if(this.isSlid){
49219             this.afterSlideIn();
49220             skipAnim = true;
49221         }
49222         this.collapsed = false;
49223         if(this.config.animate && skipAnim !== true){
49224             this.animateExpand();
49225         }else{
49226             this.el.show();
49227             if(this.split){
49228                 this.split.el.show();
49229             }
49230             this.collapsedEl.setLocation(-2000,-2000);
49231             this.collapsedEl.hide();
49232             this.fireEvent("invalidated", this);
49233             this.fireEvent("expanded", this);
49234         }
49235     },
49236
49237     animateExpand : function(){
49238         // overridden
49239     },
49240
49241     initTabs : function()
49242     {
49243         this.bodyEl.setStyle("overflow", "hidden");
49244         var ts = new Roo.TabPanel(
49245                 this.bodyEl.dom,
49246                 {
49247                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49248                     disableTooltips: this.config.disableTabTips,
49249                     toolbar : this.config.toolbar
49250                 }
49251         );
49252         if(this.config.hideTabs){
49253             ts.stripWrap.setDisplayed(false);
49254         }
49255         this.tabs = ts;
49256         ts.resizeTabs = this.config.resizeTabs === true;
49257         ts.minTabWidth = this.config.minTabWidth || 40;
49258         ts.maxTabWidth = this.config.maxTabWidth || 250;
49259         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49260         ts.monitorResize = false;
49261         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49262         ts.bodyEl.addClass('x-layout-tabs-body');
49263         this.panels.each(this.initPanelAsTab, this);
49264     },
49265
49266     initPanelAsTab : function(panel){
49267         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49268                     this.config.closeOnTab && panel.isClosable());
49269         if(panel.tabTip !== undefined){
49270             ti.setTooltip(panel.tabTip);
49271         }
49272         ti.on("activate", function(){
49273               this.setActivePanel(panel);
49274         }, this);
49275         if(this.config.closeOnTab){
49276             ti.on("beforeclose", function(t, e){
49277                 e.cancel = true;
49278                 this.remove(panel);
49279             }, this);
49280         }
49281         return ti;
49282     },
49283
49284     updatePanelTitle : function(panel, title){
49285         if(this.activePanel == panel){
49286             this.updateTitle(title);
49287         }
49288         if(this.tabs){
49289             var ti = this.tabs.getTab(panel.getEl().id);
49290             ti.setText(title);
49291             if(panel.tabTip !== undefined){
49292                 ti.setTooltip(panel.tabTip);
49293             }
49294         }
49295     },
49296
49297     updateTitle : function(title){
49298         if(this.titleTextEl && !this.config.title){
49299             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49300         }
49301     },
49302
49303     setActivePanel : function(panel){
49304         panel = this.getPanel(panel);
49305         if(this.activePanel && this.activePanel != panel){
49306             this.activePanel.setActiveState(false);
49307         }
49308         this.activePanel = panel;
49309         panel.setActiveState(true);
49310         if(this.panelSize){
49311             panel.setSize(this.panelSize.width, this.panelSize.height);
49312         }
49313         if(this.closeBtn){
49314             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49315         }
49316         this.updateTitle(panel.getTitle());
49317         if(this.tabs){
49318             this.fireEvent("invalidated", this);
49319         }
49320         this.fireEvent("panelactivated", this, panel);
49321     },
49322
49323     /**
49324      * Shows the specified panel.
49325      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49326      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49327      */
49328     showPanel : function(panel){
49329         if(panel = this.getPanel(panel)){
49330             if(this.tabs){
49331                 var tab = this.tabs.getTab(panel.getEl().id);
49332                 if(tab.isHidden()){
49333                     this.tabs.unhideTab(tab.id);
49334                 }
49335                 tab.activate();
49336             }else{
49337                 this.setActivePanel(panel);
49338             }
49339         }
49340         return panel;
49341     },
49342
49343     /**
49344      * Get the active panel for this region.
49345      * @return {Roo.ContentPanel} The active panel or null
49346      */
49347     getActivePanel : function(){
49348         return this.activePanel;
49349     },
49350
49351     validateVisibility : function(){
49352         if(this.panels.getCount() < 1){
49353             this.updateTitle("&#160;");
49354             this.closeBtn.hide();
49355             this.hide();
49356         }else{
49357             if(!this.isVisible()){
49358                 this.show();
49359             }
49360         }
49361     },
49362
49363     /**
49364      * Adds the passed ContentPanel(s) to this region.
49365      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49366      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49367      */
49368     add : function(panel){
49369         if(arguments.length > 1){
49370             for(var i = 0, len = arguments.length; i < len; i++) {
49371                 this.add(arguments[i]);
49372             }
49373             return null;
49374         }
49375         if(this.hasPanel(panel)){
49376             this.showPanel(panel);
49377             return panel;
49378         }
49379         panel.setRegion(this);
49380         this.panels.add(panel);
49381         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49382             this.bodyEl.dom.appendChild(panel.getEl().dom);
49383             if(panel.background !== true){
49384                 this.setActivePanel(panel);
49385             }
49386             this.fireEvent("paneladded", this, panel);
49387             return panel;
49388         }
49389         if(!this.tabs){
49390             this.initTabs();
49391         }else{
49392             this.initPanelAsTab(panel);
49393         }
49394         if(panel.background !== true){
49395             this.tabs.activate(panel.getEl().id);
49396         }
49397         this.fireEvent("paneladded", this, panel);
49398         return panel;
49399     },
49400
49401     /**
49402      * Hides the tab for the specified panel.
49403      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49404      */
49405     hidePanel : function(panel){
49406         if(this.tabs && (panel = this.getPanel(panel))){
49407             this.tabs.hideTab(panel.getEl().id);
49408         }
49409     },
49410
49411     /**
49412      * Unhides the tab for a previously hidden panel.
49413      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49414      */
49415     unhidePanel : function(panel){
49416         if(this.tabs && (panel = this.getPanel(panel))){
49417             this.tabs.unhideTab(panel.getEl().id);
49418         }
49419     },
49420
49421     clearPanels : function(){
49422         while(this.panels.getCount() > 0){
49423              this.remove(this.panels.first());
49424         }
49425     },
49426
49427     /**
49428      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49429      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49430      * @param {Boolean} preservePanel Overrides the config preservePanel option
49431      * @return {Roo.ContentPanel} The panel that was removed
49432      */
49433     remove : function(panel, preservePanel){
49434         panel = this.getPanel(panel);
49435         if(!panel){
49436             return null;
49437         }
49438         var e = {};
49439         this.fireEvent("beforeremove", this, panel, e);
49440         if(e.cancel === true){
49441             return null;
49442         }
49443         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49444         var panelId = panel.getId();
49445         this.panels.removeKey(panelId);
49446         if(preservePanel){
49447             document.body.appendChild(panel.getEl().dom);
49448         }
49449         if(this.tabs){
49450             this.tabs.removeTab(panel.getEl().id);
49451         }else if (!preservePanel){
49452             this.bodyEl.dom.removeChild(panel.getEl().dom);
49453         }
49454         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49455             var p = this.panels.first();
49456             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49457             tempEl.appendChild(p.getEl().dom);
49458             this.bodyEl.update("");
49459             this.bodyEl.dom.appendChild(p.getEl().dom);
49460             tempEl = null;
49461             this.updateTitle(p.getTitle());
49462             this.tabs = null;
49463             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49464             this.setActivePanel(p);
49465         }
49466         panel.setRegion(null);
49467         if(this.activePanel == panel){
49468             this.activePanel = null;
49469         }
49470         if(this.config.autoDestroy !== false && preservePanel !== true){
49471             try{panel.destroy();}catch(e){}
49472         }
49473         this.fireEvent("panelremoved", this, panel);
49474         return panel;
49475     },
49476
49477     /**
49478      * Returns the TabPanel component used by this region
49479      * @return {Roo.TabPanel}
49480      */
49481     getTabs : function(){
49482         return this.tabs;
49483     },
49484
49485     createTool : function(parentEl, className){
49486         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49487             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49488         btn.addClassOnOver("x-layout-tools-button-over");
49489         return btn;
49490     }
49491 });/*
49492  * Based on:
49493  * Ext JS Library 1.1.1
49494  * Copyright(c) 2006-2007, Ext JS, LLC.
49495  *
49496  * Originally Released Under LGPL - original licence link has changed is not relivant.
49497  *
49498  * Fork - LGPL
49499  * <script type="text/javascript">
49500  */
49501  
49502
49503
49504 /**
49505  * @class Roo.SplitLayoutRegion
49506  * @extends Roo.LayoutRegion
49507  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49508  */
49509 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49510     this.cursor = cursor;
49511     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49512 };
49513
49514 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49515     splitTip : "Drag to resize.",
49516     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49517     useSplitTips : false,
49518
49519     applyConfig : function(config){
49520         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49521         if(config.split){
49522             if(!this.split){
49523                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49524                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49525                 /** The SplitBar for this region 
49526                 * @type Roo.SplitBar */
49527                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49528                 this.split.on("moved", this.onSplitMove, this);
49529                 this.split.useShim = config.useShim === true;
49530                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49531                 if(this.useSplitTips){
49532                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49533                 }
49534                 if(config.collapsible){
49535                     this.split.el.on("dblclick", this.collapse,  this);
49536                 }
49537             }
49538             if(typeof config.minSize != "undefined"){
49539                 this.split.minSize = config.minSize;
49540             }
49541             if(typeof config.maxSize != "undefined"){
49542                 this.split.maxSize = config.maxSize;
49543             }
49544             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49545                 this.hideSplitter();
49546             }
49547         }
49548     },
49549
49550     getHMaxSize : function(){
49551          var cmax = this.config.maxSize || 10000;
49552          var center = this.mgr.getRegion("center");
49553          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49554     },
49555
49556     getVMaxSize : function(){
49557          var cmax = this.config.maxSize || 10000;
49558          var center = this.mgr.getRegion("center");
49559          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49560     },
49561
49562     onSplitMove : function(split, newSize){
49563         this.fireEvent("resized", this, newSize);
49564     },
49565     
49566     /** 
49567      * Returns the {@link Roo.SplitBar} for this region.
49568      * @return {Roo.SplitBar}
49569      */
49570     getSplitBar : function(){
49571         return this.split;
49572     },
49573     
49574     hide : function(){
49575         this.hideSplitter();
49576         Roo.SplitLayoutRegion.superclass.hide.call(this);
49577     },
49578
49579     hideSplitter : function(){
49580         if(this.split){
49581             this.split.el.setLocation(-2000,-2000);
49582             this.split.el.hide();
49583         }
49584     },
49585
49586     show : function(){
49587         if(this.split){
49588             this.split.el.show();
49589         }
49590         Roo.SplitLayoutRegion.superclass.show.call(this);
49591     },
49592     
49593     beforeSlide: function(){
49594         if(Roo.isGecko){// firefox overflow auto bug workaround
49595             this.bodyEl.clip();
49596             if(this.tabs) this.tabs.bodyEl.clip();
49597             if(this.activePanel){
49598                 this.activePanel.getEl().clip();
49599                 
49600                 if(this.activePanel.beforeSlide){
49601                     this.activePanel.beforeSlide();
49602                 }
49603             }
49604         }
49605     },
49606     
49607     afterSlide : function(){
49608         if(Roo.isGecko){// firefox overflow auto bug workaround
49609             this.bodyEl.unclip();
49610             if(this.tabs) this.tabs.bodyEl.unclip();
49611             if(this.activePanel){
49612                 this.activePanel.getEl().unclip();
49613                 if(this.activePanel.afterSlide){
49614                     this.activePanel.afterSlide();
49615                 }
49616             }
49617         }
49618     },
49619
49620     initAutoHide : function(){
49621         if(this.autoHide !== false){
49622             if(!this.autoHideHd){
49623                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49624                 this.autoHideHd = {
49625                     "mouseout": function(e){
49626                         if(!e.within(this.el, true)){
49627                             st.delay(500);
49628                         }
49629                     },
49630                     "mouseover" : function(e){
49631                         st.cancel();
49632                     },
49633                     scope : this
49634                 };
49635             }
49636             this.el.on(this.autoHideHd);
49637         }
49638     },
49639
49640     clearAutoHide : function(){
49641         if(this.autoHide !== false){
49642             this.el.un("mouseout", this.autoHideHd.mouseout);
49643             this.el.un("mouseover", this.autoHideHd.mouseover);
49644         }
49645     },
49646
49647     clearMonitor : function(){
49648         Roo.get(document).un("click", this.slideInIf, this);
49649     },
49650
49651     // these names are backwards but not changed for compat
49652     slideOut : function(){
49653         if(this.isSlid || this.el.hasActiveFx()){
49654             return;
49655         }
49656         this.isSlid = true;
49657         if(this.collapseBtn){
49658             this.collapseBtn.hide();
49659         }
49660         this.closeBtnState = this.closeBtn.getStyle('display');
49661         this.closeBtn.hide();
49662         if(this.stickBtn){
49663             this.stickBtn.show();
49664         }
49665         this.el.show();
49666         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
49667         this.beforeSlide();
49668         this.el.setStyle("z-index", 10001);
49669         this.el.slideIn(this.getSlideAnchor(), {
49670             callback: function(){
49671                 this.afterSlide();
49672                 this.initAutoHide();
49673                 Roo.get(document).on("click", this.slideInIf, this);
49674                 this.fireEvent("slideshow", this);
49675             },
49676             scope: this,
49677             block: true
49678         });
49679     },
49680
49681     afterSlideIn : function(){
49682         this.clearAutoHide();
49683         this.isSlid = false;
49684         this.clearMonitor();
49685         this.el.setStyle("z-index", "");
49686         if(this.collapseBtn){
49687             this.collapseBtn.show();
49688         }
49689         this.closeBtn.setStyle('display', this.closeBtnState);
49690         if(this.stickBtn){
49691             this.stickBtn.hide();
49692         }
49693         this.fireEvent("slidehide", this);
49694     },
49695
49696     slideIn : function(cb){
49697         if(!this.isSlid || this.el.hasActiveFx()){
49698             Roo.callback(cb);
49699             return;
49700         }
49701         this.isSlid = false;
49702         this.beforeSlide();
49703         this.el.slideOut(this.getSlideAnchor(), {
49704             callback: function(){
49705                 this.el.setLeftTop(-10000, -10000);
49706                 this.afterSlide();
49707                 this.afterSlideIn();
49708                 Roo.callback(cb);
49709             },
49710             scope: this,
49711             block: true
49712         });
49713     },
49714     
49715     slideInIf : function(e){
49716         if(!e.within(this.el)){
49717             this.slideIn();
49718         }
49719     },
49720
49721     animateCollapse : function(){
49722         this.beforeSlide();
49723         this.el.setStyle("z-index", 20000);
49724         var anchor = this.getSlideAnchor();
49725         this.el.slideOut(anchor, {
49726             callback : function(){
49727                 this.el.setStyle("z-index", "");
49728                 this.collapsedEl.slideIn(anchor, {duration:.3});
49729                 this.afterSlide();
49730                 this.el.setLocation(-10000,-10000);
49731                 this.el.hide();
49732                 this.fireEvent("collapsed", this);
49733             },
49734             scope: this,
49735             block: true
49736         });
49737     },
49738
49739     animateExpand : function(){
49740         this.beforeSlide();
49741         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
49742         this.el.setStyle("z-index", 20000);
49743         this.collapsedEl.hide({
49744             duration:.1
49745         });
49746         this.el.slideIn(this.getSlideAnchor(), {
49747             callback : function(){
49748                 this.el.setStyle("z-index", "");
49749                 this.afterSlide();
49750                 if(this.split){
49751                     this.split.el.show();
49752                 }
49753                 this.fireEvent("invalidated", this);
49754                 this.fireEvent("expanded", this);
49755             },
49756             scope: this,
49757             block: true
49758         });
49759     },
49760
49761     anchors : {
49762         "west" : "left",
49763         "east" : "right",
49764         "north" : "top",
49765         "south" : "bottom"
49766     },
49767
49768     sanchors : {
49769         "west" : "l",
49770         "east" : "r",
49771         "north" : "t",
49772         "south" : "b"
49773     },
49774
49775     canchors : {
49776         "west" : "tl-tr",
49777         "east" : "tr-tl",
49778         "north" : "tl-bl",
49779         "south" : "bl-tl"
49780     },
49781
49782     getAnchor : function(){
49783         return this.anchors[this.position];
49784     },
49785
49786     getCollapseAnchor : function(){
49787         return this.canchors[this.position];
49788     },
49789
49790     getSlideAnchor : function(){
49791         return this.sanchors[this.position];
49792     },
49793
49794     getAlignAdj : function(){
49795         var cm = this.cmargins;
49796         switch(this.position){
49797             case "west":
49798                 return [0, 0];
49799             break;
49800             case "east":
49801                 return [0, 0];
49802             break;
49803             case "north":
49804                 return [0, 0];
49805             break;
49806             case "south":
49807                 return [0, 0];
49808             break;
49809         }
49810     },
49811
49812     getExpandAdj : function(){
49813         var c = this.collapsedEl, cm = this.cmargins;
49814         switch(this.position){
49815             case "west":
49816                 return [-(cm.right+c.getWidth()+cm.left), 0];
49817             break;
49818             case "east":
49819                 return [cm.right+c.getWidth()+cm.left, 0];
49820             break;
49821             case "north":
49822                 return [0, -(cm.top+cm.bottom+c.getHeight())];
49823             break;
49824             case "south":
49825                 return [0, cm.top+cm.bottom+c.getHeight()];
49826             break;
49827         }
49828     }
49829 });/*
49830  * Based on:
49831  * Ext JS Library 1.1.1
49832  * Copyright(c) 2006-2007, Ext JS, LLC.
49833  *
49834  * Originally Released Under LGPL - original licence link has changed is not relivant.
49835  *
49836  * Fork - LGPL
49837  * <script type="text/javascript">
49838  */
49839 /*
49840  * These classes are private internal classes
49841  */
49842 Roo.CenterLayoutRegion = function(mgr, config){
49843     Roo.LayoutRegion.call(this, mgr, config, "center");
49844     this.visible = true;
49845     this.minWidth = config.minWidth || 20;
49846     this.minHeight = config.minHeight || 20;
49847 };
49848
49849 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
49850     hide : function(){
49851         // center panel can't be hidden
49852     },
49853     
49854     show : function(){
49855         // center panel can't be hidden
49856     },
49857     
49858     getMinWidth: function(){
49859         return this.minWidth;
49860     },
49861     
49862     getMinHeight: function(){
49863         return this.minHeight;
49864     }
49865 });
49866
49867
49868 Roo.NorthLayoutRegion = function(mgr, config){
49869     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
49870     if(this.split){
49871         this.split.placement = Roo.SplitBar.TOP;
49872         this.split.orientation = Roo.SplitBar.VERTICAL;
49873         this.split.el.addClass("x-layout-split-v");
49874     }
49875     var size = config.initialSize || config.height;
49876     if(typeof size != "undefined"){
49877         this.el.setHeight(size);
49878     }
49879 };
49880 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
49881     orientation: Roo.SplitBar.VERTICAL,
49882     getBox : function(){
49883         if(this.collapsed){
49884             return this.collapsedEl.getBox();
49885         }
49886         var box = this.el.getBox();
49887         if(this.split){
49888             box.height += this.split.el.getHeight();
49889         }
49890         return box;
49891     },
49892     
49893     updateBox : function(box){
49894         if(this.split && !this.collapsed){
49895             box.height -= this.split.el.getHeight();
49896             this.split.el.setLeft(box.x);
49897             this.split.el.setTop(box.y+box.height);
49898             this.split.el.setWidth(box.width);
49899         }
49900         if(this.collapsed){
49901             this.updateBody(box.width, null);
49902         }
49903         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49904     }
49905 });
49906
49907 Roo.SouthLayoutRegion = function(mgr, config){
49908     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
49909     if(this.split){
49910         this.split.placement = Roo.SplitBar.BOTTOM;
49911         this.split.orientation = Roo.SplitBar.VERTICAL;
49912         this.split.el.addClass("x-layout-split-v");
49913     }
49914     var size = config.initialSize || config.height;
49915     if(typeof size != "undefined"){
49916         this.el.setHeight(size);
49917     }
49918 };
49919 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
49920     orientation: Roo.SplitBar.VERTICAL,
49921     getBox : function(){
49922         if(this.collapsed){
49923             return this.collapsedEl.getBox();
49924         }
49925         var box = this.el.getBox();
49926         if(this.split){
49927             var sh = this.split.el.getHeight();
49928             box.height += sh;
49929             box.y -= sh;
49930         }
49931         return box;
49932     },
49933     
49934     updateBox : function(box){
49935         if(this.split && !this.collapsed){
49936             var sh = this.split.el.getHeight();
49937             box.height -= sh;
49938             box.y += sh;
49939             this.split.el.setLeft(box.x);
49940             this.split.el.setTop(box.y-sh);
49941             this.split.el.setWidth(box.width);
49942         }
49943         if(this.collapsed){
49944             this.updateBody(box.width, null);
49945         }
49946         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49947     }
49948 });
49949
49950 Roo.EastLayoutRegion = function(mgr, config){
49951     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
49952     if(this.split){
49953         this.split.placement = Roo.SplitBar.RIGHT;
49954         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49955         this.split.el.addClass("x-layout-split-h");
49956     }
49957     var size = config.initialSize || config.width;
49958     if(typeof size != "undefined"){
49959         this.el.setWidth(size);
49960     }
49961 };
49962 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
49963     orientation: Roo.SplitBar.HORIZONTAL,
49964     getBox : function(){
49965         if(this.collapsed){
49966             return this.collapsedEl.getBox();
49967         }
49968         var box = this.el.getBox();
49969         if(this.split){
49970             var sw = this.split.el.getWidth();
49971             box.width += sw;
49972             box.x -= sw;
49973         }
49974         return box;
49975     },
49976
49977     updateBox : function(box){
49978         if(this.split && !this.collapsed){
49979             var sw = this.split.el.getWidth();
49980             box.width -= sw;
49981             this.split.el.setLeft(box.x);
49982             this.split.el.setTop(box.y);
49983             this.split.el.setHeight(box.height);
49984             box.x += sw;
49985         }
49986         if(this.collapsed){
49987             this.updateBody(null, box.height);
49988         }
49989         Roo.LayoutRegion.prototype.updateBox.call(this, box);
49990     }
49991 });
49992
49993 Roo.WestLayoutRegion = function(mgr, config){
49994     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
49995     if(this.split){
49996         this.split.placement = Roo.SplitBar.LEFT;
49997         this.split.orientation = Roo.SplitBar.HORIZONTAL;
49998         this.split.el.addClass("x-layout-split-h");
49999     }
50000     var size = config.initialSize || config.width;
50001     if(typeof size != "undefined"){
50002         this.el.setWidth(size);
50003     }
50004 };
50005 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50006     orientation: Roo.SplitBar.HORIZONTAL,
50007     getBox : function(){
50008         if(this.collapsed){
50009             return this.collapsedEl.getBox();
50010         }
50011         var box = this.el.getBox();
50012         if(this.split){
50013             box.width += this.split.el.getWidth();
50014         }
50015         return box;
50016     },
50017     
50018     updateBox : function(box){
50019         if(this.split && !this.collapsed){
50020             var sw = this.split.el.getWidth();
50021             box.width -= sw;
50022             this.split.el.setLeft(box.x+box.width);
50023             this.split.el.setTop(box.y);
50024             this.split.el.setHeight(box.height);
50025         }
50026         if(this.collapsed){
50027             this.updateBody(null, box.height);
50028         }
50029         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50030     }
50031 });
50032 /*
50033  * Based on:
50034  * Ext JS Library 1.1.1
50035  * Copyright(c) 2006-2007, Ext JS, LLC.
50036  *
50037  * Originally Released Under LGPL - original licence link has changed is not relivant.
50038  *
50039  * Fork - LGPL
50040  * <script type="text/javascript">
50041  */
50042  
50043  
50044 /*
50045  * Private internal class for reading and applying state
50046  */
50047 Roo.LayoutStateManager = function(layout){
50048      // default empty state
50049      this.state = {
50050         north: {},
50051         south: {},
50052         east: {},
50053         west: {}       
50054     };
50055 };
50056
50057 Roo.LayoutStateManager.prototype = {
50058     init : function(layout, provider){
50059         this.provider = provider;
50060         var state = provider.get(layout.id+"-layout-state");
50061         if(state){
50062             var wasUpdating = layout.isUpdating();
50063             if(!wasUpdating){
50064                 layout.beginUpdate();
50065             }
50066             for(var key in state){
50067                 if(typeof state[key] != "function"){
50068                     var rstate = state[key];
50069                     var r = layout.getRegion(key);
50070                     if(r && rstate){
50071                         if(rstate.size){
50072                             r.resizeTo(rstate.size);
50073                         }
50074                         if(rstate.collapsed == true){
50075                             r.collapse(true);
50076                         }else{
50077                             r.expand(null, true);
50078                         }
50079                     }
50080                 }
50081             }
50082             if(!wasUpdating){
50083                 layout.endUpdate();
50084             }
50085             this.state = state; 
50086         }
50087         this.layout = layout;
50088         layout.on("regionresized", this.onRegionResized, this);
50089         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50090         layout.on("regionexpanded", this.onRegionExpanded, this);
50091     },
50092     
50093     storeState : function(){
50094         this.provider.set(this.layout.id+"-layout-state", this.state);
50095     },
50096     
50097     onRegionResized : function(region, newSize){
50098         this.state[region.getPosition()].size = newSize;
50099         this.storeState();
50100     },
50101     
50102     onRegionCollapsed : function(region){
50103         this.state[region.getPosition()].collapsed = true;
50104         this.storeState();
50105     },
50106     
50107     onRegionExpanded : function(region){
50108         this.state[region.getPosition()].collapsed = false;
50109         this.storeState();
50110     }
50111 };/*
50112  * Based on:
50113  * Ext JS Library 1.1.1
50114  * Copyright(c) 2006-2007, Ext JS, LLC.
50115  *
50116  * Originally Released Under LGPL - original licence link has changed is not relivant.
50117  *
50118  * Fork - LGPL
50119  * <script type="text/javascript">
50120  */
50121 /**
50122  * @class Roo.ContentPanel
50123  * @extends Roo.util.Observable
50124  * A basic ContentPanel element.
50125  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50126  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50127  * @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
50128  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50129  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50130  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50131  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50132  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50133  * @cfg {String} title          The title for this panel
50134  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50135  * @cfg {String} url            Calls {@link #setUrl} with this value
50136  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50137  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50138  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50139  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50140
50141  * @constructor
50142  * Create a new ContentPanel.
50143  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50144  * @param {String/Object} config A string to set only the title or a config object
50145  * @param {String} content (optional) Set the HTML content for this panel
50146  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50147  */
50148 Roo.ContentPanel = function(el, config, content){
50149     
50150      
50151     /*
50152     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50153         config = el;
50154         el = Roo.id();
50155     }
50156     if (config && config.parentLayout) { 
50157         el = config.parentLayout.el.createChild(); 
50158     }
50159     */
50160     if(el.autoCreate){ // xtype is available if this is called from factory
50161         config = el;
50162         el = Roo.id();
50163     }
50164     this.el = Roo.get(el);
50165     if(!this.el && config && config.autoCreate){
50166         if(typeof config.autoCreate == "object"){
50167             if(!config.autoCreate.id){
50168                 config.autoCreate.id = config.id||el;
50169             }
50170             this.el = Roo.DomHelper.append(document.body,
50171                         config.autoCreate, true);
50172         }else{
50173             this.el = Roo.DomHelper.append(document.body,
50174                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50175         }
50176     }
50177     this.closable = false;
50178     this.loaded = false;
50179     this.active = false;
50180     if(typeof config == "string"){
50181         this.title = config;
50182     }else{
50183         Roo.apply(this, config);
50184     }
50185     
50186     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50187         this.wrapEl = this.el.wrap();
50188         this.toolbar.container = this.el.insertSibling(false, 'before');
50189         this.toolbar = new Roo.Toolbar(this.toolbar);
50190     }
50191     
50192     // xtype created footer. - not sure if will work as we normally have to render first..
50193     if (this.footer && !this.footer.el && this.footer.xtype) {
50194         if (!this.wrapEl) {
50195             this.wrapEl = this.el.wrap();
50196         }
50197     
50198         this.footer.container = this.wrapEl.createChild();
50199          
50200         this.footer = Roo.factory(this.footer, Roo);
50201         
50202     }
50203     
50204     if(this.resizeEl){
50205         this.resizeEl = Roo.get(this.resizeEl, true);
50206     }else{
50207         this.resizeEl = this.el;
50208     }
50209     // handle view.xtype
50210     
50211  
50212     
50213     
50214     this.addEvents({
50215         /**
50216          * @event activate
50217          * Fires when this panel is activated. 
50218          * @param {Roo.ContentPanel} this
50219          */
50220         "activate" : true,
50221         /**
50222          * @event deactivate
50223          * Fires when this panel is activated. 
50224          * @param {Roo.ContentPanel} this
50225          */
50226         "deactivate" : true,
50227
50228         /**
50229          * @event resize
50230          * Fires when this panel is resized if fitToFrame is true.
50231          * @param {Roo.ContentPanel} this
50232          * @param {Number} width The width after any component adjustments
50233          * @param {Number} height The height after any component adjustments
50234          */
50235         "resize" : true,
50236         
50237          /**
50238          * @event render
50239          * Fires when this tab is created
50240          * @param {Roo.ContentPanel} this
50241          */
50242         "render" : true
50243         
50244         
50245         
50246     });
50247     
50248
50249     
50250     
50251     if(this.autoScroll){
50252         this.resizeEl.setStyle("overflow", "auto");
50253     } else {
50254         // fix randome scrolling
50255         this.el.on('scroll', function() {
50256             Roo.log('fix random scolling');
50257             this.scrollTo('top',0); 
50258         });
50259     }
50260     content = content || this.content;
50261     if(content){
50262         this.setContent(content);
50263     }
50264     if(config && config.url){
50265         this.setUrl(this.url, this.params, this.loadOnce);
50266     }
50267     
50268     
50269     
50270     Roo.ContentPanel.superclass.constructor.call(this);
50271     
50272     if (this.view && typeof(this.view.xtype) != 'undefined') {
50273         this.view.el = this.el.appendChild(document.createElement("div"));
50274         this.view = Roo.factory(this.view); 
50275         this.view.render  &&  this.view.render(false, '');  
50276     }
50277     
50278     
50279     this.fireEvent('render', this);
50280 };
50281
50282 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50283     tabTip:'',
50284     setRegion : function(region){
50285         this.region = region;
50286         if(region){
50287            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50288         }else{
50289            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50290         } 
50291     },
50292     
50293     /**
50294      * Returns the toolbar for this Panel if one was configured. 
50295      * @return {Roo.Toolbar} 
50296      */
50297     getToolbar : function(){
50298         return this.toolbar;
50299     },
50300     
50301     setActiveState : function(active){
50302         this.active = active;
50303         if(!active){
50304             this.fireEvent("deactivate", this);
50305         }else{
50306             this.fireEvent("activate", this);
50307         }
50308     },
50309     /**
50310      * Updates this panel's element
50311      * @param {String} content The new content
50312      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50313     */
50314     setContent : function(content, loadScripts){
50315         this.el.update(content, loadScripts);
50316     },
50317
50318     ignoreResize : function(w, h){
50319         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50320             return true;
50321         }else{
50322             this.lastSize = {width: w, height: h};
50323             return false;
50324         }
50325     },
50326     /**
50327      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50328      * @return {Roo.UpdateManager} The UpdateManager
50329      */
50330     getUpdateManager : function(){
50331         return this.el.getUpdateManager();
50332     },
50333      /**
50334      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50335      * @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:
50336 <pre><code>
50337 panel.load({
50338     url: "your-url.php",
50339     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50340     callback: yourFunction,
50341     scope: yourObject, //(optional scope)
50342     discardUrl: false,
50343     nocache: false,
50344     text: "Loading...",
50345     timeout: 30,
50346     scripts: false
50347 });
50348 </code></pre>
50349      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50350      * 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.
50351      * @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}
50352      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50353      * @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.
50354      * @return {Roo.ContentPanel} this
50355      */
50356     load : function(){
50357         var um = this.el.getUpdateManager();
50358         um.update.apply(um, arguments);
50359         return this;
50360     },
50361
50362
50363     /**
50364      * 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.
50365      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50366      * @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)
50367      * @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)
50368      * @return {Roo.UpdateManager} The UpdateManager
50369      */
50370     setUrl : function(url, params, loadOnce){
50371         if(this.refreshDelegate){
50372             this.removeListener("activate", this.refreshDelegate);
50373         }
50374         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50375         this.on("activate", this.refreshDelegate);
50376         return this.el.getUpdateManager();
50377     },
50378     
50379     _handleRefresh : function(url, params, loadOnce){
50380         if(!loadOnce || !this.loaded){
50381             var updater = this.el.getUpdateManager();
50382             updater.update(url, params, this._setLoaded.createDelegate(this));
50383         }
50384     },
50385     
50386     _setLoaded : function(){
50387         this.loaded = true;
50388     }, 
50389     
50390     /**
50391      * Returns this panel's id
50392      * @return {String} 
50393      */
50394     getId : function(){
50395         return this.el.id;
50396     },
50397     
50398     /** 
50399      * Returns this panel's element - used by regiosn to add.
50400      * @return {Roo.Element} 
50401      */
50402     getEl : function(){
50403         return this.wrapEl || this.el;
50404     },
50405     
50406     adjustForComponents : function(width, height)
50407     {
50408         //Roo.log('adjustForComponents ');
50409         if(this.resizeEl != this.el){
50410             width -= this.el.getFrameWidth('lr');
50411             height -= this.el.getFrameWidth('tb');
50412         }
50413         if(this.toolbar){
50414             var te = this.toolbar.getEl();
50415             height -= te.getHeight();
50416             te.setWidth(width);
50417         }
50418         if(this.footer){
50419             var te = this.footer.getEl();
50420             Roo.log("footer:" + te.getHeight());
50421             
50422             height -= te.getHeight();
50423             te.setWidth(width);
50424         }
50425         
50426         
50427         if(this.adjustments){
50428             width += this.adjustments[0];
50429             height += this.adjustments[1];
50430         }
50431         return {"width": width, "height": height};
50432     },
50433     
50434     setSize : function(width, height){
50435         if(this.fitToFrame && !this.ignoreResize(width, height)){
50436             if(this.fitContainer && this.resizeEl != this.el){
50437                 this.el.setSize(width, height);
50438             }
50439             var size = this.adjustForComponents(width, height);
50440             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50441             this.fireEvent('resize', this, size.width, size.height);
50442         }
50443     },
50444     
50445     /**
50446      * Returns this panel's title
50447      * @return {String} 
50448      */
50449     getTitle : function(){
50450         return this.title;
50451     },
50452     
50453     /**
50454      * Set this panel's title
50455      * @param {String} title
50456      */
50457     setTitle : function(title){
50458         this.title = title;
50459         if(this.region){
50460             this.region.updatePanelTitle(this, title);
50461         }
50462     },
50463     
50464     /**
50465      * Returns true is this panel was configured to be closable
50466      * @return {Boolean} 
50467      */
50468     isClosable : function(){
50469         return this.closable;
50470     },
50471     
50472     beforeSlide : function(){
50473         this.el.clip();
50474         this.resizeEl.clip();
50475     },
50476     
50477     afterSlide : function(){
50478         this.el.unclip();
50479         this.resizeEl.unclip();
50480     },
50481     
50482     /**
50483      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50484      *   Will fail silently if the {@link #setUrl} method has not been called.
50485      *   This does not activate the panel, just updates its content.
50486      */
50487     refresh : function(){
50488         if(this.refreshDelegate){
50489            this.loaded = false;
50490            this.refreshDelegate();
50491         }
50492     },
50493     
50494     /**
50495      * Destroys this panel
50496      */
50497     destroy : function(){
50498         this.el.removeAllListeners();
50499         var tempEl = document.createElement("span");
50500         tempEl.appendChild(this.el.dom);
50501         tempEl.innerHTML = "";
50502         this.el.remove();
50503         this.el = null;
50504     },
50505     
50506     /**
50507      * form - if the content panel contains a form - this is a reference to it.
50508      * @type {Roo.form.Form}
50509      */
50510     form : false,
50511     /**
50512      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50513      *    This contains a reference to it.
50514      * @type {Roo.View}
50515      */
50516     view : false,
50517     
50518       /**
50519      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50520      * <pre><code>
50521
50522 layout.addxtype({
50523        xtype : 'Form',
50524        items: [ .... ]
50525    }
50526 );
50527
50528 </code></pre>
50529      * @param {Object} cfg Xtype definition of item to add.
50530      */
50531     
50532     addxtype : function(cfg) {
50533         // add form..
50534         if (cfg.xtype.match(/^Form$/)) {
50535             
50536             var el;
50537             //if (this.footer) {
50538             //    el = this.footer.container.insertSibling(false, 'before');
50539             //} else {
50540                 el = this.el.createChild();
50541             //}
50542
50543             this.form = new  Roo.form.Form(cfg);
50544             
50545             
50546             if ( this.form.allItems.length) this.form.render(el.dom);
50547             return this.form;
50548         }
50549         // should only have one of theses..
50550         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50551             // views.. should not be just added - used named prop 'view''
50552             
50553             cfg.el = this.el.appendChild(document.createElement("div"));
50554             // factory?
50555             
50556             var ret = new Roo.factory(cfg);
50557              
50558              ret.render && ret.render(false, ''); // render blank..
50559             this.view = ret;
50560             return ret;
50561         }
50562         return false;
50563     }
50564 });
50565
50566 /**
50567  * @class Roo.GridPanel
50568  * @extends Roo.ContentPanel
50569  * @constructor
50570  * Create a new GridPanel.
50571  * @param {Roo.grid.Grid} grid The grid for this panel
50572  * @param {String/Object} config A string to set only the panel's title, or a config object
50573  */
50574 Roo.GridPanel = function(grid, config){
50575     
50576   
50577     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50578         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50579         
50580     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50581     
50582     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50583     
50584     if(this.toolbar){
50585         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50586     }
50587     // xtype created footer. - not sure if will work as we normally have to render first..
50588     if (this.footer && !this.footer.el && this.footer.xtype) {
50589         
50590         this.footer.container = this.grid.getView().getFooterPanel(true);
50591         this.footer.dataSource = this.grid.dataSource;
50592         this.footer = Roo.factory(this.footer, Roo);
50593         
50594     }
50595     
50596     grid.monitorWindowResize = false; // turn off autosizing
50597     grid.autoHeight = false;
50598     grid.autoWidth = false;
50599     this.grid = grid;
50600     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50601 };
50602
50603 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50604     getId : function(){
50605         return this.grid.id;
50606     },
50607     
50608     /**
50609      * Returns the grid for this panel
50610      * @return {Roo.grid.Grid} 
50611      */
50612     getGrid : function(){
50613         return this.grid;    
50614     },
50615     
50616     setSize : function(width, height){
50617         if(!this.ignoreResize(width, height)){
50618             var grid = this.grid;
50619             var size = this.adjustForComponents(width, height);
50620             grid.getGridEl().setSize(size.width, size.height);
50621             grid.autoSize();
50622         }
50623     },
50624     
50625     beforeSlide : function(){
50626         this.grid.getView().scroller.clip();
50627     },
50628     
50629     afterSlide : function(){
50630         this.grid.getView().scroller.unclip();
50631     },
50632     
50633     destroy : function(){
50634         this.grid.destroy();
50635         delete this.grid;
50636         Roo.GridPanel.superclass.destroy.call(this); 
50637     }
50638 });
50639
50640
50641 /**
50642  * @class Roo.NestedLayoutPanel
50643  * @extends Roo.ContentPanel
50644  * @constructor
50645  * Create a new NestedLayoutPanel.
50646  * 
50647  * 
50648  * @param {Roo.BorderLayout} layout The layout for this panel
50649  * @param {String/Object} config A string to set only the title or a config object
50650  */
50651 Roo.NestedLayoutPanel = function(layout, config)
50652 {
50653     // construct with only one argument..
50654     /* FIXME - implement nicer consturctors
50655     if (layout.layout) {
50656         config = layout;
50657         layout = config.layout;
50658         delete config.layout;
50659     }
50660     if (layout.xtype && !layout.getEl) {
50661         // then layout needs constructing..
50662         layout = Roo.factory(layout, Roo);
50663     }
50664     */
50665     
50666     
50667     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
50668     
50669     layout.monitorWindowResize = false; // turn off autosizing
50670     this.layout = layout;
50671     this.layout.getEl().addClass("x-layout-nested-layout");
50672     
50673     
50674     
50675     
50676 };
50677
50678 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
50679
50680     setSize : function(width, height){
50681         if(!this.ignoreResize(width, height)){
50682             var size = this.adjustForComponents(width, height);
50683             var el = this.layout.getEl();
50684             el.setSize(size.width, size.height);
50685             var touch = el.dom.offsetWidth;
50686             this.layout.layout();
50687             // ie requires a double layout on the first pass
50688             if(Roo.isIE && !this.initialized){
50689                 this.initialized = true;
50690                 this.layout.layout();
50691             }
50692         }
50693     },
50694     
50695     // activate all subpanels if not currently active..
50696     
50697     setActiveState : function(active){
50698         this.active = active;
50699         if(!active){
50700             this.fireEvent("deactivate", this);
50701             return;
50702         }
50703         
50704         this.fireEvent("activate", this);
50705         // not sure if this should happen before or after..
50706         if (!this.layout) {
50707             return; // should not happen..
50708         }
50709         var reg = false;
50710         for (var r in this.layout.regions) {
50711             reg = this.layout.getRegion(r);
50712             if (reg.getActivePanel()) {
50713                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
50714                 reg.setActivePanel(reg.getActivePanel());
50715                 continue;
50716             }
50717             if (!reg.panels.length) {
50718                 continue;
50719             }
50720             reg.showPanel(reg.getPanel(0));
50721         }
50722         
50723         
50724         
50725         
50726     },
50727     
50728     /**
50729      * Returns the nested BorderLayout for this panel
50730      * @return {Roo.BorderLayout} 
50731      */
50732     getLayout : function(){
50733         return this.layout;
50734     },
50735     
50736      /**
50737      * Adds a xtype elements to the layout of the nested panel
50738      * <pre><code>
50739
50740 panel.addxtype({
50741        xtype : 'ContentPanel',
50742        region: 'west',
50743        items: [ .... ]
50744    }
50745 );
50746
50747 panel.addxtype({
50748         xtype : 'NestedLayoutPanel',
50749         region: 'west',
50750         layout: {
50751            center: { },
50752            west: { }   
50753         },
50754         items : [ ... list of content panels or nested layout panels.. ]
50755    }
50756 );
50757 </code></pre>
50758      * @param {Object} cfg Xtype definition of item to add.
50759      */
50760     addxtype : function(cfg) {
50761         return this.layout.addxtype(cfg);
50762     
50763     }
50764 });
50765
50766 Roo.ScrollPanel = function(el, config, content){
50767     config = config || {};
50768     config.fitToFrame = true;
50769     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
50770     
50771     this.el.dom.style.overflow = "hidden";
50772     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
50773     this.el.removeClass("x-layout-inactive-content");
50774     this.el.on("mousewheel", this.onWheel, this);
50775
50776     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
50777     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
50778     up.unselectable(); down.unselectable();
50779     up.on("click", this.scrollUp, this);
50780     down.on("click", this.scrollDown, this);
50781     up.addClassOnOver("x-scroller-btn-over");
50782     down.addClassOnOver("x-scroller-btn-over");
50783     up.addClassOnClick("x-scroller-btn-click");
50784     down.addClassOnClick("x-scroller-btn-click");
50785     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
50786
50787     this.resizeEl = this.el;
50788     this.el = wrap; this.up = up; this.down = down;
50789 };
50790
50791 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
50792     increment : 100,
50793     wheelIncrement : 5,
50794     scrollUp : function(){
50795         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
50796     },
50797
50798     scrollDown : function(){
50799         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
50800     },
50801
50802     afterScroll : function(){
50803         var el = this.resizeEl;
50804         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
50805         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50806         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
50807     },
50808
50809     setSize : function(){
50810         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
50811         this.afterScroll();
50812     },
50813
50814     onWheel : function(e){
50815         var d = e.getWheelDelta();
50816         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
50817         this.afterScroll();
50818         e.stopEvent();
50819     },
50820
50821     setContent : function(content, loadScripts){
50822         this.resizeEl.update(content, loadScripts);
50823     }
50824
50825 });
50826
50827
50828
50829
50830
50831
50832
50833
50834
50835 /**
50836  * @class Roo.TreePanel
50837  * @extends Roo.ContentPanel
50838  * @constructor
50839  * Create a new TreePanel. - defaults to fit/scoll contents.
50840  * @param {String/Object} config A string to set only the panel's title, or a config object
50841  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
50842  */
50843 Roo.TreePanel = function(config){
50844     var el = config.el;
50845     var tree = config.tree;
50846     delete config.tree; 
50847     delete config.el; // hopefull!
50848     
50849     // wrapper for IE7 strict & safari scroll issue
50850     
50851     var treeEl = el.createChild();
50852     config.resizeEl = treeEl;
50853     
50854     
50855     
50856     Roo.TreePanel.superclass.constructor.call(this, el, config);
50857  
50858  
50859     this.tree = new Roo.tree.TreePanel(treeEl , tree);
50860     //console.log(tree);
50861     this.on('activate', function()
50862     {
50863         if (this.tree.rendered) {
50864             return;
50865         }
50866         //console.log('render tree');
50867         this.tree.render();
50868     });
50869     // this should not be needed.. - it's actually the 'el' that resizes?
50870     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
50871     
50872     //this.on('resize',  function (cp, w, h) {
50873     //        this.tree.innerCt.setWidth(w);
50874     //        this.tree.innerCt.setHeight(h);
50875     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
50876     //});
50877
50878         
50879     
50880 };
50881
50882 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
50883     fitToFrame : true,
50884     autoScroll : true
50885 });
50886
50887
50888
50889
50890
50891
50892
50893
50894
50895
50896
50897 /*
50898  * Based on:
50899  * Ext JS Library 1.1.1
50900  * Copyright(c) 2006-2007, Ext JS, LLC.
50901  *
50902  * Originally Released Under LGPL - original licence link has changed is not relivant.
50903  *
50904  * Fork - LGPL
50905  * <script type="text/javascript">
50906  */
50907  
50908
50909 /**
50910  * @class Roo.ReaderLayout
50911  * @extends Roo.BorderLayout
50912  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
50913  * center region containing two nested regions (a top one for a list view and one for item preview below),
50914  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
50915  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
50916  * expedites the setup of the overall layout and regions for this common application style.
50917  * Example:
50918  <pre><code>
50919 var reader = new Roo.ReaderLayout();
50920 var CP = Roo.ContentPanel;  // shortcut for adding
50921
50922 reader.beginUpdate();
50923 reader.add("north", new CP("north", "North"));
50924 reader.add("west", new CP("west", {title: "West"}));
50925 reader.add("east", new CP("east", {title: "East"}));
50926
50927 reader.regions.listView.add(new CP("listView", "List"));
50928 reader.regions.preview.add(new CP("preview", "Preview"));
50929 reader.endUpdate();
50930 </code></pre>
50931 * @constructor
50932 * Create a new ReaderLayout
50933 * @param {Object} config Configuration options
50934 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
50935 * document.body if omitted)
50936 */
50937 Roo.ReaderLayout = function(config, renderTo){
50938     var c = config || {size:{}};
50939     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
50940         north: c.north !== false ? Roo.apply({
50941             split:false,
50942             initialSize: 32,
50943             titlebar: false
50944         }, c.north) : false,
50945         west: c.west !== false ? Roo.apply({
50946             split:true,
50947             initialSize: 200,
50948             minSize: 175,
50949             maxSize: 400,
50950             titlebar: true,
50951             collapsible: true,
50952             animate: true,
50953             margins:{left:5,right:0,bottom:5,top:5},
50954             cmargins:{left:5,right:5,bottom:5,top:5}
50955         }, c.west) : false,
50956         east: c.east !== false ? Roo.apply({
50957             split:true,
50958             initialSize: 200,
50959             minSize: 175,
50960             maxSize: 400,
50961             titlebar: true,
50962             collapsible: true,
50963             animate: true,
50964             margins:{left:0,right:5,bottom:5,top:5},
50965             cmargins:{left:5,right:5,bottom:5,top:5}
50966         }, c.east) : false,
50967         center: Roo.apply({
50968             tabPosition: 'top',
50969             autoScroll:false,
50970             closeOnTab: true,
50971             titlebar:false,
50972             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
50973         }, c.center)
50974     });
50975
50976     this.el.addClass('x-reader');
50977
50978     this.beginUpdate();
50979
50980     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
50981         south: c.preview !== false ? Roo.apply({
50982             split:true,
50983             initialSize: 200,
50984             minSize: 100,
50985             autoScroll:true,
50986             collapsible:true,
50987             titlebar: true,
50988             cmargins:{top:5,left:0, right:0, bottom:0}
50989         }, c.preview) : false,
50990         center: Roo.apply({
50991             autoScroll:false,
50992             titlebar:false,
50993             minHeight:200
50994         }, c.listView)
50995     });
50996     this.add('center', new Roo.NestedLayoutPanel(inner,
50997             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
50998
50999     this.endUpdate();
51000
51001     this.regions.preview = inner.getRegion('south');
51002     this.regions.listView = inner.getRegion('center');
51003 };
51004
51005 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51006  * Based on:
51007  * Ext JS Library 1.1.1
51008  * Copyright(c) 2006-2007, Ext JS, LLC.
51009  *
51010  * Originally Released Under LGPL - original licence link has changed is not relivant.
51011  *
51012  * Fork - LGPL
51013  * <script type="text/javascript">
51014  */
51015  
51016 /**
51017  * @class Roo.grid.Grid
51018  * @extends Roo.util.Observable
51019  * This class represents the primary interface of a component based grid control.
51020  * <br><br>Usage:<pre><code>
51021  var grid = new Roo.grid.Grid("my-container-id", {
51022      ds: myDataStore,
51023      cm: myColModel,
51024      selModel: mySelectionModel,
51025      autoSizeColumns: true,
51026      monitorWindowResize: false,
51027      trackMouseOver: true
51028  });
51029  // set any options
51030  grid.render();
51031  * </code></pre>
51032  * <b>Common Problems:</b><br/>
51033  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51034  * element will correct this<br/>
51035  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51036  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51037  * are unpredictable.<br/>
51038  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51039  * grid to calculate dimensions/offsets.<br/>
51040   * @constructor
51041  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51042  * The container MUST have some type of size defined for the grid to fill. The container will be
51043  * automatically set to position relative if it isn't already.
51044  * @param {Object} config A config object that sets properties on this grid.
51045  */
51046 Roo.grid.Grid = function(container, config){
51047         // initialize the container
51048         this.container = Roo.get(container);
51049         this.container.update("");
51050         this.container.setStyle("overflow", "hidden");
51051     this.container.addClass('x-grid-container');
51052
51053     this.id = this.container.id;
51054
51055     Roo.apply(this, config);
51056     // check and correct shorthanded configs
51057     if(this.ds){
51058         this.dataSource = this.ds;
51059         delete this.ds;
51060     }
51061     if(this.cm){
51062         this.colModel = this.cm;
51063         delete this.cm;
51064     }
51065     if(this.sm){
51066         this.selModel = this.sm;
51067         delete this.sm;
51068     }
51069
51070     if (this.selModel) {
51071         this.selModel = Roo.factory(this.selModel, Roo.grid);
51072         this.sm = this.selModel;
51073         this.sm.xmodule = this.xmodule || false;
51074     }
51075     if (typeof(this.colModel.config) == 'undefined') {
51076         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51077         this.cm = this.colModel;
51078         this.cm.xmodule = this.xmodule || false;
51079     }
51080     if (this.dataSource) {
51081         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51082         this.ds = this.dataSource;
51083         this.ds.xmodule = this.xmodule || false;
51084          
51085     }
51086     
51087     
51088     
51089     if(this.width){
51090         this.container.setWidth(this.width);
51091     }
51092
51093     if(this.height){
51094         this.container.setHeight(this.height);
51095     }
51096     /** @private */
51097         this.addEvents({
51098         // raw events
51099         /**
51100          * @event click
51101          * The raw click event for the entire grid.
51102          * @param {Roo.EventObject} e
51103          */
51104         "click" : true,
51105         /**
51106          * @event dblclick
51107          * The raw dblclick event for the entire grid.
51108          * @param {Roo.EventObject} e
51109          */
51110         "dblclick" : true,
51111         /**
51112          * @event contextmenu
51113          * The raw contextmenu event for the entire grid.
51114          * @param {Roo.EventObject} e
51115          */
51116         "contextmenu" : true,
51117         /**
51118          * @event mousedown
51119          * The raw mousedown event for the entire grid.
51120          * @param {Roo.EventObject} e
51121          */
51122         "mousedown" : true,
51123         /**
51124          * @event mouseup
51125          * The raw mouseup event for the entire grid.
51126          * @param {Roo.EventObject} e
51127          */
51128         "mouseup" : true,
51129         /**
51130          * @event mouseover
51131          * The raw mouseover event for the entire grid.
51132          * @param {Roo.EventObject} e
51133          */
51134         "mouseover" : true,
51135         /**
51136          * @event mouseout
51137          * The raw mouseout event for the entire grid.
51138          * @param {Roo.EventObject} e
51139          */
51140         "mouseout" : true,
51141         /**
51142          * @event keypress
51143          * The raw keypress event for the entire grid.
51144          * @param {Roo.EventObject} e
51145          */
51146         "keypress" : true,
51147         /**
51148          * @event keydown
51149          * The raw keydown event for the entire grid.
51150          * @param {Roo.EventObject} e
51151          */
51152         "keydown" : true,
51153
51154         // custom events
51155
51156         /**
51157          * @event cellclick
51158          * Fires when a cell is clicked
51159          * @param {Grid} this
51160          * @param {Number} rowIndex
51161          * @param {Number} columnIndex
51162          * @param {Roo.EventObject} e
51163          */
51164         "cellclick" : true,
51165         /**
51166          * @event celldblclick
51167          * Fires when a cell is double clicked
51168          * @param {Grid} this
51169          * @param {Number} rowIndex
51170          * @param {Number} columnIndex
51171          * @param {Roo.EventObject} e
51172          */
51173         "celldblclick" : true,
51174         /**
51175          * @event rowclick
51176          * Fires when a row is clicked
51177          * @param {Grid} this
51178          * @param {Number} rowIndex
51179          * @param {Roo.EventObject} e
51180          */
51181         "rowclick" : true,
51182         /**
51183          * @event rowdblclick
51184          * Fires when a row is double clicked
51185          * @param {Grid} this
51186          * @param {Number} rowIndex
51187          * @param {Roo.EventObject} e
51188          */
51189         "rowdblclick" : true,
51190         /**
51191          * @event headerclick
51192          * Fires when a header is clicked
51193          * @param {Grid} this
51194          * @param {Number} columnIndex
51195          * @param {Roo.EventObject} e
51196          */
51197         "headerclick" : true,
51198         /**
51199          * @event headerdblclick
51200          * Fires when a header cell is double clicked
51201          * @param {Grid} this
51202          * @param {Number} columnIndex
51203          * @param {Roo.EventObject} e
51204          */
51205         "headerdblclick" : true,
51206         /**
51207          * @event rowcontextmenu
51208          * Fires when a row is right clicked
51209          * @param {Grid} this
51210          * @param {Number} rowIndex
51211          * @param {Roo.EventObject} e
51212          */
51213         "rowcontextmenu" : true,
51214         /**
51215          * @event cellcontextmenu
51216          * Fires when a cell is right clicked
51217          * @param {Grid} this
51218          * @param {Number} rowIndex
51219          * @param {Number} cellIndex
51220          * @param {Roo.EventObject} e
51221          */
51222          "cellcontextmenu" : true,
51223         /**
51224          * @event headercontextmenu
51225          * Fires when a header is right clicked
51226          * @param {Grid} this
51227          * @param {Number} columnIndex
51228          * @param {Roo.EventObject} e
51229          */
51230         "headercontextmenu" : true,
51231         /**
51232          * @event bodyscroll
51233          * Fires when the body element is scrolled
51234          * @param {Number} scrollLeft
51235          * @param {Number} scrollTop
51236          */
51237         "bodyscroll" : true,
51238         /**
51239          * @event columnresize
51240          * Fires when the user resizes a column
51241          * @param {Number} columnIndex
51242          * @param {Number} newSize
51243          */
51244         "columnresize" : true,
51245         /**
51246          * @event columnmove
51247          * Fires when the user moves a column
51248          * @param {Number} oldIndex
51249          * @param {Number} newIndex
51250          */
51251         "columnmove" : true,
51252         /**
51253          * @event startdrag
51254          * Fires when row(s) start being dragged
51255          * @param {Grid} this
51256          * @param {Roo.GridDD} dd The drag drop object
51257          * @param {event} e The raw browser event
51258          */
51259         "startdrag" : true,
51260         /**
51261          * @event enddrag
51262          * Fires when a drag operation is complete
51263          * @param {Grid} this
51264          * @param {Roo.GridDD} dd The drag drop object
51265          * @param {event} e The raw browser event
51266          */
51267         "enddrag" : true,
51268         /**
51269          * @event dragdrop
51270          * Fires when dragged row(s) are dropped on a valid DD target
51271          * @param {Grid} this
51272          * @param {Roo.GridDD} dd The drag drop object
51273          * @param {String} targetId The target drag drop object
51274          * @param {event} e The raw browser event
51275          */
51276         "dragdrop" : true,
51277         /**
51278          * @event dragover
51279          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51280          * @param {Grid} this
51281          * @param {Roo.GridDD} dd The drag drop object
51282          * @param {String} targetId The target drag drop object
51283          * @param {event} e The raw browser event
51284          */
51285         "dragover" : true,
51286         /**
51287          * @event dragenter
51288          *  Fires when the dragged row(s) first cross another DD target while being dragged
51289          * @param {Grid} this
51290          * @param {Roo.GridDD} dd The drag drop object
51291          * @param {String} targetId The target drag drop object
51292          * @param {event} e The raw browser event
51293          */
51294         "dragenter" : true,
51295         /**
51296          * @event dragout
51297          * Fires when the dragged row(s) leave another DD target while being dragged
51298          * @param {Grid} this
51299          * @param {Roo.GridDD} dd The drag drop object
51300          * @param {String} targetId The target drag drop object
51301          * @param {event} e The raw browser event
51302          */
51303         "dragout" : true,
51304         /**
51305          * @event rowclass
51306          * Fires when a row is rendered, so you can change add a style to it.
51307          * @param {GridView} gridview   The grid view
51308          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51309          */
51310         'rowclass' : true,
51311
51312         /**
51313          * @event render
51314          * Fires when the grid is rendered
51315          * @param {Grid} grid
51316          */
51317         'render' : true
51318     });
51319
51320     Roo.grid.Grid.superclass.constructor.call(this);
51321 };
51322 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51323     
51324     /**
51325      * @cfg {String} ddGroup - drag drop group.
51326      */
51327
51328     /**
51329      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51330      */
51331     minColumnWidth : 25,
51332
51333     /**
51334      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51335      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51336      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51337      */
51338     autoSizeColumns : false,
51339
51340     /**
51341      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51342      */
51343     autoSizeHeaders : true,
51344
51345     /**
51346      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51347      */
51348     monitorWindowResize : true,
51349
51350     /**
51351      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51352      * rows measured to get a columns size. Default is 0 (all rows).
51353      */
51354     maxRowsToMeasure : 0,
51355
51356     /**
51357      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51358      */
51359     trackMouseOver : true,
51360
51361     /**
51362     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51363     */
51364     
51365     /**
51366     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51367     */
51368     enableDragDrop : false,
51369     
51370     /**
51371     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51372     */
51373     enableColumnMove : true,
51374     
51375     /**
51376     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51377     */
51378     enableColumnHide : true,
51379     
51380     /**
51381     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51382     */
51383     enableRowHeightSync : false,
51384     
51385     /**
51386     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51387     */
51388     stripeRows : true,
51389     
51390     /**
51391     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51392     */
51393     autoHeight : false,
51394
51395     /**
51396      * @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.
51397      */
51398     autoExpandColumn : false,
51399
51400     /**
51401     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51402     * Default is 50.
51403     */
51404     autoExpandMin : 50,
51405
51406     /**
51407     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51408     */
51409     autoExpandMax : 1000,
51410
51411     /**
51412     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51413     */
51414     view : null,
51415
51416     /**
51417     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51418     */
51419     loadMask : false,
51420     /**
51421     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51422     */
51423     dropTarget: false,
51424     
51425    
51426     
51427     // private
51428     rendered : false,
51429
51430     /**
51431     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51432     * of a fixed width. Default is false.
51433     */
51434     /**
51435     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51436     */
51437     /**
51438      * Called once after all setup has been completed and the grid is ready to be rendered.
51439      * @return {Roo.grid.Grid} this
51440      */
51441     render : function()
51442     {
51443         var c = this.container;
51444         // try to detect autoHeight/width mode
51445         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51446             this.autoHeight = true;
51447         }
51448         var view = this.getView();
51449         view.init(this);
51450
51451         c.on("click", this.onClick, this);
51452         c.on("dblclick", this.onDblClick, this);
51453         c.on("contextmenu", this.onContextMenu, this);
51454         c.on("keydown", this.onKeyDown, this);
51455         if (Roo.isTouch) {
51456             c.on("touchstart", this.onTouchStart, this);
51457         }
51458
51459         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51460
51461         this.getSelectionModel().init(this);
51462
51463         view.render();
51464
51465         if(this.loadMask){
51466             this.loadMask = new Roo.LoadMask(this.container,
51467                     Roo.apply({store:this.dataSource}, this.loadMask));
51468         }
51469         
51470         
51471         if (this.toolbar && this.toolbar.xtype) {
51472             this.toolbar.container = this.getView().getHeaderPanel(true);
51473             this.toolbar = new Roo.Toolbar(this.toolbar);
51474         }
51475         if (this.footer && this.footer.xtype) {
51476             this.footer.dataSource = this.getDataSource();
51477             this.footer.container = this.getView().getFooterPanel(true);
51478             this.footer = Roo.factory(this.footer, Roo);
51479         }
51480         if (this.dropTarget && this.dropTarget.xtype) {
51481             delete this.dropTarget.xtype;
51482             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51483         }
51484         
51485         
51486         this.rendered = true;
51487         this.fireEvent('render', this);
51488         return this;
51489     },
51490
51491         /**
51492          * Reconfigures the grid to use a different Store and Column Model.
51493          * The View will be bound to the new objects and refreshed.
51494          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51495          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51496          */
51497     reconfigure : function(dataSource, colModel){
51498         if(this.loadMask){
51499             this.loadMask.destroy();
51500             this.loadMask = new Roo.LoadMask(this.container,
51501                     Roo.apply({store:dataSource}, this.loadMask));
51502         }
51503         this.view.bind(dataSource, colModel);
51504         this.dataSource = dataSource;
51505         this.colModel = colModel;
51506         this.view.refresh(true);
51507     },
51508
51509     // private
51510     onKeyDown : function(e){
51511         this.fireEvent("keydown", e);
51512     },
51513
51514     /**
51515      * Destroy this grid.
51516      * @param {Boolean} removeEl True to remove the element
51517      */
51518     destroy : function(removeEl, keepListeners){
51519         if(this.loadMask){
51520             this.loadMask.destroy();
51521         }
51522         var c = this.container;
51523         c.removeAllListeners();
51524         this.view.destroy();
51525         this.colModel.purgeListeners();
51526         if(!keepListeners){
51527             this.purgeListeners();
51528         }
51529         c.update("");
51530         if(removeEl === true){
51531             c.remove();
51532         }
51533     },
51534
51535     // private
51536     processEvent : function(name, e){
51537         // does this fire select???
51538         Roo.log('grid:processEvent '  + name);
51539         
51540         if (name != 'touchstart' ) {
51541             this.fireEvent(name, e);    
51542         }
51543         
51544         var t = e.getTarget();
51545         var v = this.view;
51546         var header = v.findHeaderIndex(t);
51547         if(header !== false){
51548             this.fireEvent("header" + (name == 'touchstart' ? 'click' : name), this, header, e);
51549         }else{
51550             var row = v.findRowIndex(t);
51551             var cell = v.findCellIndex(t);
51552             if (name == 'touchstart') {
51553                 // first touch is always a click.
51554                 // hopefull this happens after selection is updated.?
51555                 name = false;
51556                 
51557                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51558                     var cs = this.selModel.getSelectedCell();
51559                     if (row == cs[0] && cell == cs[1]){
51560                         name = 'dblclick';
51561                     }
51562                 }
51563                 if (typeof(this.selModel.getSelections) != 'undefined') {
51564                     var cs = this.selModel.getSelections();
51565                     var ds = this.dataSource;
51566                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51567                         name = 'dblclick';
51568                     }
51569                 }
51570                 if (!name) {
51571                     return;
51572                 }
51573             }
51574             
51575             
51576             if(row !== false){
51577                 this.fireEvent("row" + name, this, row, e);
51578                 if(cell !== false){
51579                     this.fireEvent("cell" + name, this, row, cell, e);
51580                 }
51581             }
51582         }
51583     },
51584
51585     // private
51586     onClick : function(e){
51587         this.processEvent("click", e);
51588     },
51589    // private
51590     onTouchStart : function(e){
51591         this.processEvent("touchstart", e);
51592     },
51593
51594     // private
51595     onContextMenu : function(e, t){
51596         this.processEvent("contextmenu", e);
51597     },
51598
51599     // private
51600     onDblClick : function(e){
51601         this.processEvent("dblclick", e);
51602     },
51603
51604     // private
51605     walkCells : function(row, col, step, fn, scope){
51606         var cm = this.colModel, clen = cm.getColumnCount();
51607         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51608         if(step < 0){
51609             if(col < 0){
51610                 row--;
51611                 first = false;
51612             }
51613             while(row >= 0){
51614                 if(!first){
51615                     col = clen-1;
51616                 }
51617                 first = false;
51618                 while(col >= 0){
51619                     if(fn.call(scope || this, row, col, cm) === true){
51620                         return [row, col];
51621                     }
51622                     col--;
51623                 }
51624                 row--;
51625             }
51626         } else {
51627             if(col >= clen){
51628                 row++;
51629                 first = false;
51630             }
51631             while(row < rlen){
51632                 if(!first){
51633                     col = 0;
51634                 }
51635                 first = false;
51636                 while(col < clen){
51637                     if(fn.call(scope || this, row, col, cm) === true){
51638                         return [row, col];
51639                     }
51640                     col++;
51641                 }
51642                 row++;
51643             }
51644         }
51645         return null;
51646     },
51647
51648     // private
51649     getSelections : function(){
51650         return this.selModel.getSelections();
51651     },
51652
51653     /**
51654      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
51655      * but if manual update is required this method will initiate it.
51656      */
51657     autoSize : function(){
51658         if(this.rendered){
51659             this.view.layout();
51660             if(this.view.adjustForScroll){
51661                 this.view.adjustForScroll();
51662             }
51663         }
51664     },
51665
51666     /**
51667      * Returns the grid's underlying element.
51668      * @return {Element} The element
51669      */
51670     getGridEl : function(){
51671         return this.container;
51672     },
51673
51674     // private for compatibility, overridden by editor grid
51675     stopEditing : function(){},
51676
51677     /**
51678      * Returns the grid's SelectionModel.
51679      * @return {SelectionModel}
51680      */
51681     getSelectionModel : function(){
51682         if(!this.selModel){
51683             this.selModel = new Roo.grid.RowSelectionModel();
51684         }
51685         return this.selModel;
51686     },
51687
51688     /**
51689      * Returns the grid's DataSource.
51690      * @return {DataSource}
51691      */
51692     getDataSource : function(){
51693         return this.dataSource;
51694     },
51695
51696     /**
51697      * Returns the grid's ColumnModel.
51698      * @return {ColumnModel}
51699      */
51700     getColumnModel : function(){
51701         return this.colModel;
51702     },
51703
51704     /**
51705      * Returns the grid's GridView object.
51706      * @return {GridView}
51707      */
51708     getView : function(){
51709         if(!this.view){
51710             this.view = new Roo.grid.GridView(this.viewConfig);
51711         }
51712         return this.view;
51713     },
51714     /**
51715      * Called to get grid's drag proxy text, by default returns this.ddText.
51716      * @return {String}
51717      */
51718     getDragDropText : function(){
51719         var count = this.selModel.getCount();
51720         return String.format(this.ddText, count, count == 1 ? '' : 's');
51721     }
51722 });
51723 /**
51724  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
51725  * %0 is replaced with the number of selected rows.
51726  * @type String
51727  */
51728 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
51729  * Based on:
51730  * Ext JS Library 1.1.1
51731  * Copyright(c) 2006-2007, Ext JS, LLC.
51732  *
51733  * Originally Released Under LGPL - original licence link has changed is not relivant.
51734  *
51735  * Fork - LGPL
51736  * <script type="text/javascript">
51737  */
51738  
51739 Roo.grid.AbstractGridView = function(){
51740         this.grid = null;
51741         
51742         this.events = {
51743             "beforerowremoved" : true,
51744             "beforerowsinserted" : true,
51745             "beforerefresh" : true,
51746             "rowremoved" : true,
51747             "rowsinserted" : true,
51748             "rowupdated" : true,
51749             "refresh" : true
51750         };
51751     Roo.grid.AbstractGridView.superclass.constructor.call(this);
51752 };
51753
51754 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
51755     rowClass : "x-grid-row",
51756     cellClass : "x-grid-cell",
51757     tdClass : "x-grid-td",
51758     hdClass : "x-grid-hd",
51759     splitClass : "x-grid-hd-split",
51760     
51761         init: function(grid){
51762         this.grid = grid;
51763                 var cid = this.grid.getGridEl().id;
51764         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
51765         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
51766         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
51767         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
51768         },
51769         
51770         getColumnRenderers : function(){
51771         var renderers = [];
51772         var cm = this.grid.colModel;
51773         var colCount = cm.getColumnCount();
51774         for(var i = 0; i < colCount; i++){
51775             renderers[i] = cm.getRenderer(i);
51776         }
51777         return renderers;
51778     },
51779     
51780     getColumnIds : function(){
51781         var ids = [];
51782         var cm = this.grid.colModel;
51783         var colCount = cm.getColumnCount();
51784         for(var i = 0; i < colCount; i++){
51785             ids[i] = cm.getColumnId(i);
51786         }
51787         return ids;
51788     },
51789     
51790     getDataIndexes : function(){
51791         if(!this.indexMap){
51792             this.indexMap = this.buildIndexMap();
51793         }
51794         return this.indexMap.colToData;
51795     },
51796     
51797     getColumnIndexByDataIndex : function(dataIndex){
51798         if(!this.indexMap){
51799             this.indexMap = this.buildIndexMap();
51800         }
51801         return this.indexMap.dataToCol[dataIndex];
51802     },
51803     
51804     /**
51805      * Set a css style for a column dynamically. 
51806      * @param {Number} colIndex The index of the column
51807      * @param {String} name The css property name
51808      * @param {String} value The css value
51809      */
51810     setCSSStyle : function(colIndex, name, value){
51811         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
51812         Roo.util.CSS.updateRule(selector, name, value);
51813     },
51814     
51815     generateRules : function(cm){
51816         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
51817         Roo.util.CSS.removeStyleSheet(rulesId);
51818         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51819             var cid = cm.getColumnId(i);
51820             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
51821                          this.tdSelector, cid, " {\n}\n",
51822                          this.hdSelector, cid, " {\n}\n",
51823                          this.splitSelector, cid, " {\n}\n");
51824         }
51825         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51826     }
51827 });/*
51828  * Based on:
51829  * Ext JS Library 1.1.1
51830  * Copyright(c) 2006-2007, Ext JS, LLC.
51831  *
51832  * Originally Released Under LGPL - original licence link has changed is not relivant.
51833  *
51834  * Fork - LGPL
51835  * <script type="text/javascript">
51836  */
51837
51838 // private
51839 // This is a support class used internally by the Grid components
51840 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
51841     this.grid = grid;
51842     this.view = grid.getView();
51843     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51844     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
51845     if(hd2){
51846         this.setHandleElId(Roo.id(hd));
51847         this.setOuterHandleElId(Roo.id(hd2));
51848     }
51849     this.scroll = false;
51850 };
51851 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
51852     maxDragWidth: 120,
51853     getDragData : function(e){
51854         var t = Roo.lib.Event.getTarget(e);
51855         var h = this.view.findHeaderCell(t);
51856         if(h){
51857             return {ddel: h.firstChild, header:h};
51858         }
51859         return false;
51860     },
51861
51862     onInitDrag : function(e){
51863         this.view.headersDisabled = true;
51864         var clone = this.dragData.ddel.cloneNode(true);
51865         clone.id = Roo.id();
51866         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
51867         this.proxy.update(clone);
51868         return true;
51869     },
51870
51871     afterValidDrop : function(){
51872         var v = this.view;
51873         setTimeout(function(){
51874             v.headersDisabled = false;
51875         }, 50);
51876     },
51877
51878     afterInvalidDrop : function(){
51879         var v = this.view;
51880         setTimeout(function(){
51881             v.headersDisabled = false;
51882         }, 50);
51883     }
51884 });
51885 /*
51886  * Based on:
51887  * Ext JS Library 1.1.1
51888  * Copyright(c) 2006-2007, Ext JS, LLC.
51889  *
51890  * Originally Released Under LGPL - original licence link has changed is not relivant.
51891  *
51892  * Fork - LGPL
51893  * <script type="text/javascript">
51894  */
51895 // private
51896 // This is a support class used internally by the Grid components
51897 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
51898     this.grid = grid;
51899     this.view = grid.getView();
51900     // split the proxies so they don't interfere with mouse events
51901     this.proxyTop = Roo.DomHelper.append(document.body, {
51902         cls:"col-move-top", html:"&#160;"
51903     }, true);
51904     this.proxyBottom = Roo.DomHelper.append(document.body, {
51905         cls:"col-move-bottom", html:"&#160;"
51906     }, true);
51907     this.proxyTop.hide = this.proxyBottom.hide = function(){
51908         this.setLeftTop(-100,-100);
51909         this.setStyle("visibility", "hidden");
51910     };
51911     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
51912     // temporarily disabled
51913     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
51914     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
51915 };
51916 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
51917     proxyOffsets : [-4, -9],
51918     fly: Roo.Element.fly,
51919
51920     getTargetFromEvent : function(e){
51921         var t = Roo.lib.Event.getTarget(e);
51922         var cindex = this.view.findCellIndex(t);
51923         if(cindex !== false){
51924             return this.view.getHeaderCell(cindex);
51925         }
51926         return null;
51927     },
51928
51929     nextVisible : function(h){
51930         var v = this.view, cm = this.grid.colModel;
51931         h = h.nextSibling;
51932         while(h){
51933             if(!cm.isHidden(v.getCellIndex(h))){
51934                 return h;
51935             }
51936             h = h.nextSibling;
51937         }
51938         return null;
51939     },
51940
51941     prevVisible : function(h){
51942         var v = this.view, cm = this.grid.colModel;
51943         h = h.prevSibling;
51944         while(h){
51945             if(!cm.isHidden(v.getCellIndex(h))){
51946                 return h;
51947             }
51948             h = h.prevSibling;
51949         }
51950         return null;
51951     },
51952
51953     positionIndicator : function(h, n, e){
51954         var x = Roo.lib.Event.getPageX(e);
51955         var r = Roo.lib.Dom.getRegion(n.firstChild);
51956         var px, pt, py = r.top + this.proxyOffsets[1];
51957         if((r.right - x) <= (r.right-r.left)/2){
51958             px = r.right+this.view.borderWidth;
51959             pt = "after";
51960         }else{
51961             px = r.left;
51962             pt = "before";
51963         }
51964         var oldIndex = this.view.getCellIndex(h);
51965         var newIndex = this.view.getCellIndex(n);
51966
51967         if(this.grid.colModel.isFixed(newIndex)){
51968             return false;
51969         }
51970
51971         var locked = this.grid.colModel.isLocked(newIndex);
51972
51973         if(pt == "after"){
51974             newIndex++;
51975         }
51976         if(oldIndex < newIndex){
51977             newIndex--;
51978         }
51979         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
51980             return false;
51981         }
51982         px +=  this.proxyOffsets[0];
51983         this.proxyTop.setLeftTop(px, py);
51984         this.proxyTop.show();
51985         if(!this.bottomOffset){
51986             this.bottomOffset = this.view.mainHd.getHeight();
51987         }
51988         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
51989         this.proxyBottom.show();
51990         return pt;
51991     },
51992
51993     onNodeEnter : function(n, dd, e, data){
51994         if(data.header != n){
51995             this.positionIndicator(data.header, n, e);
51996         }
51997     },
51998
51999     onNodeOver : function(n, dd, e, data){
52000         var result = false;
52001         if(data.header != n){
52002             result = this.positionIndicator(data.header, n, e);
52003         }
52004         if(!result){
52005             this.proxyTop.hide();
52006             this.proxyBottom.hide();
52007         }
52008         return result ? this.dropAllowed : this.dropNotAllowed;
52009     },
52010
52011     onNodeOut : function(n, dd, e, data){
52012         this.proxyTop.hide();
52013         this.proxyBottom.hide();
52014     },
52015
52016     onNodeDrop : function(n, dd, e, data){
52017         var h = data.header;
52018         if(h != n){
52019             var cm = this.grid.colModel;
52020             var x = Roo.lib.Event.getPageX(e);
52021             var r = Roo.lib.Dom.getRegion(n.firstChild);
52022             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52023             var oldIndex = this.view.getCellIndex(h);
52024             var newIndex = this.view.getCellIndex(n);
52025             var locked = cm.isLocked(newIndex);
52026             if(pt == "after"){
52027                 newIndex++;
52028             }
52029             if(oldIndex < newIndex){
52030                 newIndex--;
52031             }
52032             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52033                 return false;
52034             }
52035             cm.setLocked(oldIndex, locked, true);
52036             cm.moveColumn(oldIndex, newIndex);
52037             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52038             return true;
52039         }
52040         return false;
52041     }
52042 });
52043 /*
52044  * Based on:
52045  * Ext JS Library 1.1.1
52046  * Copyright(c) 2006-2007, Ext JS, LLC.
52047  *
52048  * Originally Released Under LGPL - original licence link has changed is not relivant.
52049  *
52050  * Fork - LGPL
52051  * <script type="text/javascript">
52052  */
52053   
52054 /**
52055  * @class Roo.grid.GridView
52056  * @extends Roo.util.Observable
52057  *
52058  * @constructor
52059  * @param {Object} config
52060  */
52061 Roo.grid.GridView = function(config){
52062     Roo.grid.GridView.superclass.constructor.call(this);
52063     this.el = null;
52064
52065     Roo.apply(this, config);
52066 };
52067
52068 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52069
52070     unselectable :  'unselectable="on"',
52071     unselectableCls :  'x-unselectable',
52072     
52073     
52074     rowClass : "x-grid-row",
52075
52076     cellClass : "x-grid-col",
52077
52078     tdClass : "x-grid-td",
52079
52080     hdClass : "x-grid-hd",
52081
52082     splitClass : "x-grid-split",
52083
52084     sortClasses : ["sort-asc", "sort-desc"],
52085
52086     enableMoveAnim : false,
52087
52088     hlColor: "C3DAF9",
52089
52090     dh : Roo.DomHelper,
52091
52092     fly : Roo.Element.fly,
52093
52094     css : Roo.util.CSS,
52095
52096     borderWidth: 1,
52097
52098     splitOffset: 3,
52099
52100     scrollIncrement : 22,
52101
52102     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52103
52104     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52105
52106     bind : function(ds, cm){
52107         if(this.ds){
52108             this.ds.un("load", this.onLoad, this);
52109             this.ds.un("datachanged", this.onDataChange, this);
52110             this.ds.un("add", this.onAdd, this);
52111             this.ds.un("remove", this.onRemove, this);
52112             this.ds.un("update", this.onUpdate, this);
52113             this.ds.un("clear", this.onClear, this);
52114         }
52115         if(ds){
52116             ds.on("load", this.onLoad, this);
52117             ds.on("datachanged", this.onDataChange, this);
52118             ds.on("add", this.onAdd, this);
52119             ds.on("remove", this.onRemove, this);
52120             ds.on("update", this.onUpdate, this);
52121             ds.on("clear", this.onClear, this);
52122         }
52123         this.ds = ds;
52124
52125         if(this.cm){
52126             this.cm.un("widthchange", this.onColWidthChange, this);
52127             this.cm.un("headerchange", this.onHeaderChange, this);
52128             this.cm.un("hiddenchange", this.onHiddenChange, this);
52129             this.cm.un("columnmoved", this.onColumnMove, this);
52130             this.cm.un("columnlockchange", this.onColumnLock, this);
52131         }
52132         if(cm){
52133             this.generateRules(cm);
52134             cm.on("widthchange", this.onColWidthChange, this);
52135             cm.on("headerchange", this.onHeaderChange, this);
52136             cm.on("hiddenchange", this.onHiddenChange, this);
52137             cm.on("columnmoved", this.onColumnMove, this);
52138             cm.on("columnlockchange", this.onColumnLock, this);
52139         }
52140         this.cm = cm;
52141     },
52142
52143     init: function(grid){
52144         Roo.grid.GridView.superclass.init.call(this, grid);
52145
52146         this.bind(grid.dataSource, grid.colModel);
52147
52148         grid.on("headerclick", this.handleHeaderClick, this);
52149
52150         if(grid.trackMouseOver){
52151             grid.on("mouseover", this.onRowOver, this);
52152             grid.on("mouseout", this.onRowOut, this);
52153         }
52154         grid.cancelTextSelection = function(){};
52155         this.gridId = grid.id;
52156
52157         var tpls = this.templates || {};
52158
52159         if(!tpls.master){
52160             tpls.master = new Roo.Template(
52161                '<div class="x-grid" hidefocus="true">',
52162                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52163                   '<div class="x-grid-topbar"></div>',
52164                   '<div class="x-grid-scroller"><div></div></div>',
52165                   '<div class="x-grid-locked">',
52166                       '<div class="x-grid-header">{lockedHeader}</div>',
52167                       '<div class="x-grid-body">{lockedBody}</div>',
52168                   "</div>",
52169                   '<div class="x-grid-viewport">',
52170                       '<div class="x-grid-header">{header}</div>',
52171                       '<div class="x-grid-body">{body}</div>',
52172                   "</div>",
52173                   '<div class="x-grid-bottombar"></div>',
52174                  
52175                   '<div class="x-grid-resize-proxy">&#160;</div>',
52176                "</div>"
52177             );
52178             tpls.master.disableformats = true;
52179         }
52180
52181         if(!tpls.header){
52182             tpls.header = new Roo.Template(
52183                '<table border="0" cellspacing="0" cellpadding="0">',
52184                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52185                "</table>{splits}"
52186             );
52187             tpls.header.disableformats = true;
52188         }
52189         tpls.header.compile();
52190
52191         if(!tpls.hcell){
52192             tpls.hcell = new Roo.Template(
52193                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52194                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52195                 "</div></td>"
52196              );
52197              tpls.hcell.disableFormats = true;
52198         }
52199         tpls.hcell.compile();
52200
52201         if(!tpls.hsplit){
52202             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52203                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52204             tpls.hsplit.disableFormats = true;
52205         }
52206         tpls.hsplit.compile();
52207
52208         if(!tpls.body){
52209             tpls.body = new Roo.Template(
52210                '<table border="0" cellspacing="0" cellpadding="0">',
52211                "<tbody>{rows}</tbody>",
52212                "</table>"
52213             );
52214             tpls.body.disableFormats = true;
52215         }
52216         tpls.body.compile();
52217
52218         if(!tpls.row){
52219             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52220             tpls.row.disableFormats = true;
52221         }
52222         tpls.row.compile();
52223
52224         if(!tpls.cell){
52225             tpls.cell = new Roo.Template(
52226                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52227                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52228                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52229                 "</td>"
52230             );
52231             tpls.cell.disableFormats = true;
52232         }
52233         tpls.cell.compile();
52234
52235         this.templates = tpls;
52236     },
52237
52238     // remap these for backwards compat
52239     onColWidthChange : function(){
52240         this.updateColumns.apply(this, arguments);
52241     },
52242     onHeaderChange : function(){
52243         this.updateHeaders.apply(this, arguments);
52244     }, 
52245     onHiddenChange : function(){
52246         this.handleHiddenChange.apply(this, arguments);
52247     },
52248     onColumnMove : function(){
52249         this.handleColumnMove.apply(this, arguments);
52250     },
52251     onColumnLock : function(){
52252         this.handleLockChange.apply(this, arguments);
52253     },
52254
52255     onDataChange : function(){
52256         this.refresh();
52257         this.updateHeaderSortState();
52258     },
52259
52260     onClear : function(){
52261         this.refresh();
52262     },
52263
52264     onUpdate : function(ds, record){
52265         this.refreshRow(record);
52266     },
52267
52268     refreshRow : function(record){
52269         var ds = this.ds, index;
52270         if(typeof record == 'number'){
52271             index = record;
52272             record = ds.getAt(index);
52273         }else{
52274             index = ds.indexOf(record);
52275         }
52276         this.insertRows(ds, index, index, true);
52277         this.onRemove(ds, record, index+1, true);
52278         this.syncRowHeights(index, index);
52279         this.layout();
52280         this.fireEvent("rowupdated", this, index, record);
52281     },
52282
52283     onAdd : function(ds, records, index){
52284         this.insertRows(ds, index, index + (records.length-1));
52285     },
52286
52287     onRemove : function(ds, record, index, isUpdate){
52288         if(isUpdate !== true){
52289             this.fireEvent("beforerowremoved", this, index, record);
52290         }
52291         var bt = this.getBodyTable(), lt = this.getLockedTable();
52292         if(bt.rows[index]){
52293             bt.firstChild.removeChild(bt.rows[index]);
52294         }
52295         if(lt.rows[index]){
52296             lt.firstChild.removeChild(lt.rows[index]);
52297         }
52298         if(isUpdate !== true){
52299             this.stripeRows(index);
52300             this.syncRowHeights(index, index);
52301             this.layout();
52302             this.fireEvent("rowremoved", this, index, record);
52303         }
52304     },
52305
52306     onLoad : function(){
52307         this.scrollToTop();
52308     },
52309
52310     /**
52311      * Scrolls the grid to the top
52312      */
52313     scrollToTop : function(){
52314         if(this.scroller){
52315             this.scroller.dom.scrollTop = 0;
52316             this.syncScroll();
52317         }
52318     },
52319
52320     /**
52321      * Gets a panel in the header of the grid that can be used for toolbars etc.
52322      * After modifying the contents of this panel a call to grid.autoSize() may be
52323      * required to register any changes in size.
52324      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52325      * @return Roo.Element
52326      */
52327     getHeaderPanel : function(doShow){
52328         if(doShow){
52329             this.headerPanel.show();
52330         }
52331         return this.headerPanel;
52332     },
52333
52334     /**
52335      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52336      * After modifying the contents of this panel a call to grid.autoSize() may be
52337      * required to register any changes in size.
52338      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52339      * @return Roo.Element
52340      */
52341     getFooterPanel : function(doShow){
52342         if(doShow){
52343             this.footerPanel.show();
52344         }
52345         return this.footerPanel;
52346     },
52347
52348     initElements : function(){
52349         var E = Roo.Element;
52350         var el = this.grid.getGridEl().dom.firstChild;
52351         var cs = el.childNodes;
52352
52353         this.el = new E(el);
52354         
52355          this.focusEl = new E(el.firstChild);
52356         this.focusEl.swallowEvent("click", true);
52357         
52358         this.headerPanel = new E(cs[1]);
52359         this.headerPanel.enableDisplayMode("block");
52360
52361         this.scroller = new E(cs[2]);
52362         this.scrollSizer = new E(this.scroller.dom.firstChild);
52363
52364         this.lockedWrap = new E(cs[3]);
52365         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52366         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52367
52368         this.mainWrap = new E(cs[4]);
52369         this.mainHd = new E(this.mainWrap.dom.firstChild);
52370         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52371
52372         this.footerPanel = new E(cs[5]);
52373         this.footerPanel.enableDisplayMode("block");
52374
52375         this.resizeProxy = new E(cs[6]);
52376
52377         this.headerSelector = String.format(
52378            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52379            this.lockedHd.id, this.mainHd.id
52380         );
52381
52382         this.splitterSelector = String.format(
52383            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52384            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52385         );
52386     },
52387     idToCssName : function(s)
52388     {
52389         return s.replace(/[^a-z0-9]+/ig, '-');
52390     },
52391
52392     getHeaderCell : function(index){
52393         return Roo.DomQuery.select(this.headerSelector)[index];
52394     },
52395
52396     getHeaderCellMeasure : function(index){
52397         return this.getHeaderCell(index).firstChild;
52398     },
52399
52400     getHeaderCellText : function(index){
52401         return this.getHeaderCell(index).firstChild.firstChild;
52402     },
52403
52404     getLockedTable : function(){
52405         return this.lockedBody.dom.firstChild;
52406     },
52407
52408     getBodyTable : function(){
52409         return this.mainBody.dom.firstChild;
52410     },
52411
52412     getLockedRow : function(index){
52413         return this.getLockedTable().rows[index];
52414     },
52415
52416     getRow : function(index){
52417         return this.getBodyTable().rows[index];
52418     },
52419
52420     getRowComposite : function(index){
52421         if(!this.rowEl){
52422             this.rowEl = new Roo.CompositeElementLite();
52423         }
52424         var els = [], lrow, mrow;
52425         if(lrow = this.getLockedRow(index)){
52426             els.push(lrow);
52427         }
52428         if(mrow = this.getRow(index)){
52429             els.push(mrow);
52430         }
52431         this.rowEl.elements = els;
52432         return this.rowEl;
52433     },
52434     /**
52435      * Gets the 'td' of the cell
52436      * 
52437      * @param {Integer} rowIndex row to select
52438      * @param {Integer} colIndex column to select
52439      * 
52440      * @return {Object} 
52441      */
52442     getCell : function(rowIndex, colIndex){
52443         var locked = this.cm.getLockedCount();
52444         var source;
52445         if(colIndex < locked){
52446             source = this.lockedBody.dom.firstChild;
52447         }else{
52448             source = this.mainBody.dom.firstChild;
52449             colIndex -= locked;
52450         }
52451         return source.rows[rowIndex].childNodes[colIndex];
52452     },
52453
52454     getCellText : function(rowIndex, colIndex){
52455         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52456     },
52457
52458     getCellBox : function(cell){
52459         var b = this.fly(cell).getBox();
52460         if(Roo.isOpera){ // opera fails to report the Y
52461             b.y = cell.offsetTop + this.mainBody.getY();
52462         }
52463         return b;
52464     },
52465
52466     getCellIndex : function(cell){
52467         var id = String(cell.className).match(this.cellRE);
52468         if(id){
52469             return parseInt(id[1], 10);
52470         }
52471         return 0;
52472     },
52473
52474     findHeaderIndex : function(n){
52475         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52476         return r ? this.getCellIndex(r) : false;
52477     },
52478
52479     findHeaderCell : function(n){
52480         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52481         return r ? r : false;
52482     },
52483
52484     findRowIndex : function(n){
52485         if(!n){
52486             return false;
52487         }
52488         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52489         return r ? r.rowIndex : false;
52490     },
52491
52492     findCellIndex : function(node){
52493         var stop = this.el.dom;
52494         while(node && node != stop){
52495             if(this.findRE.test(node.className)){
52496                 return this.getCellIndex(node);
52497             }
52498             node = node.parentNode;
52499         }
52500         return false;
52501     },
52502
52503     getColumnId : function(index){
52504         return this.cm.getColumnId(index);
52505     },
52506
52507     getSplitters : function()
52508     {
52509         if(this.splitterSelector){
52510            return Roo.DomQuery.select(this.splitterSelector);
52511         }else{
52512             return null;
52513       }
52514     },
52515
52516     getSplitter : function(index){
52517         return this.getSplitters()[index];
52518     },
52519
52520     onRowOver : function(e, t){
52521         var row;
52522         if((row = this.findRowIndex(t)) !== false){
52523             this.getRowComposite(row).addClass("x-grid-row-over");
52524         }
52525     },
52526
52527     onRowOut : function(e, t){
52528         var row;
52529         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52530             this.getRowComposite(row).removeClass("x-grid-row-over");
52531         }
52532     },
52533
52534     renderHeaders : function(){
52535         var cm = this.cm;
52536         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52537         var cb = [], lb = [], sb = [], lsb = [], p = {};
52538         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52539             p.cellId = "x-grid-hd-0-" + i;
52540             p.splitId = "x-grid-csplit-0-" + i;
52541             p.id = cm.getColumnId(i);
52542             p.title = cm.getColumnTooltip(i) || "";
52543             p.value = cm.getColumnHeader(i) || "";
52544             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52545             if(!cm.isLocked(i)){
52546                 cb[cb.length] = ct.apply(p);
52547                 sb[sb.length] = st.apply(p);
52548             }else{
52549                 lb[lb.length] = ct.apply(p);
52550                 lsb[lsb.length] = st.apply(p);
52551             }
52552         }
52553         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52554                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52555     },
52556
52557     updateHeaders : function(){
52558         var html = this.renderHeaders();
52559         this.lockedHd.update(html[0]);
52560         this.mainHd.update(html[1]);
52561     },
52562
52563     /**
52564      * Focuses the specified row.
52565      * @param {Number} row The row index
52566      */
52567     focusRow : function(row)
52568     {
52569         //Roo.log('GridView.focusRow');
52570         var x = this.scroller.dom.scrollLeft;
52571         this.focusCell(row, 0, false);
52572         this.scroller.dom.scrollLeft = x;
52573     },
52574
52575     /**
52576      * Focuses the specified cell.
52577      * @param {Number} row The row index
52578      * @param {Number} col The column index
52579      * @param {Boolean} hscroll false to disable horizontal scrolling
52580      */
52581     focusCell : function(row, col, hscroll)
52582     {
52583         //Roo.log('GridView.focusCell');
52584         var el = this.ensureVisible(row, col, hscroll);
52585         this.focusEl.alignTo(el, "tl-tl");
52586         if(Roo.isGecko){
52587             this.focusEl.focus();
52588         }else{
52589             this.focusEl.focus.defer(1, this.focusEl);
52590         }
52591     },
52592
52593     /**
52594      * Scrolls the specified cell into view
52595      * @param {Number} row The row index
52596      * @param {Number} col The column index
52597      * @param {Boolean} hscroll false to disable horizontal scrolling
52598      */
52599     ensureVisible : function(row, col, hscroll)
52600     {
52601         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52602         //return null; //disable for testing.
52603         if(typeof row != "number"){
52604             row = row.rowIndex;
52605         }
52606         if(row < 0 && row >= this.ds.getCount()){
52607             return  null;
52608         }
52609         col = (col !== undefined ? col : 0);
52610         var cm = this.grid.colModel;
52611         while(cm.isHidden(col)){
52612             col++;
52613         }
52614
52615         var el = this.getCell(row, col);
52616         if(!el){
52617             return null;
52618         }
52619         var c = this.scroller.dom;
52620
52621         var ctop = parseInt(el.offsetTop, 10);
52622         var cleft = parseInt(el.offsetLeft, 10);
52623         var cbot = ctop + el.offsetHeight;
52624         var cright = cleft + el.offsetWidth;
52625         
52626         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52627         var stop = parseInt(c.scrollTop, 10);
52628         var sleft = parseInt(c.scrollLeft, 10);
52629         var sbot = stop + ch;
52630         var sright = sleft + c.clientWidth;
52631         /*
52632         Roo.log('GridView.ensureVisible:' +
52633                 ' ctop:' + ctop +
52634                 ' c.clientHeight:' + c.clientHeight +
52635                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52636                 ' stop:' + stop +
52637                 ' cbot:' + cbot +
52638                 ' sbot:' + sbot +
52639                 ' ch:' + ch  
52640                 );
52641         */
52642         if(ctop < stop){
52643              c.scrollTop = ctop;
52644             //Roo.log("set scrolltop to ctop DISABLE?");
52645         }else if(cbot > sbot){
52646             //Roo.log("set scrolltop to cbot-ch");
52647             c.scrollTop = cbot-ch;
52648         }
52649         
52650         if(hscroll !== false){
52651             if(cleft < sleft){
52652                 c.scrollLeft = cleft;
52653             }else if(cright > sright){
52654                 c.scrollLeft = cright-c.clientWidth;
52655             }
52656         }
52657          
52658         return el;
52659     },
52660
52661     updateColumns : function(){
52662         this.grid.stopEditing();
52663         var cm = this.grid.colModel, colIds = this.getColumnIds();
52664         //var totalWidth = cm.getTotalWidth();
52665         var pos = 0;
52666         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52667             //if(cm.isHidden(i)) continue;
52668             var w = cm.getColumnWidth(i);
52669             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52670             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
52671         }
52672         this.updateSplitters();
52673     },
52674
52675     generateRules : function(cm){
52676         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
52677         Roo.util.CSS.removeStyleSheet(rulesId);
52678         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52679             var cid = cm.getColumnId(i);
52680             var align = '';
52681             if(cm.config[i].align){
52682                 align = 'text-align:'+cm.config[i].align+';';
52683             }
52684             var hidden = '';
52685             if(cm.isHidden(i)){
52686                 hidden = 'display:none;';
52687             }
52688             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
52689             ruleBuf.push(
52690                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
52691                     this.hdSelector, cid, " {\n", align, width, "}\n",
52692                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
52693                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
52694         }
52695         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52696     },
52697
52698     updateSplitters : function(){
52699         var cm = this.cm, s = this.getSplitters();
52700         if(s){ // splitters not created yet
52701             var pos = 0, locked = true;
52702             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52703                 if(cm.isHidden(i)) continue;
52704                 var w = cm.getColumnWidth(i); // make sure it's a number
52705                 if(!cm.isLocked(i) && locked){
52706                     pos = 0;
52707                     locked = false;
52708                 }
52709                 pos += w;
52710                 s[i].style.left = (pos-this.splitOffset) + "px";
52711             }
52712         }
52713     },
52714
52715     handleHiddenChange : function(colModel, colIndex, hidden){
52716         if(hidden){
52717             this.hideColumn(colIndex);
52718         }else{
52719             this.unhideColumn(colIndex);
52720         }
52721     },
52722
52723     hideColumn : function(colIndex){
52724         var cid = this.getColumnId(colIndex);
52725         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
52726         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
52727         if(Roo.isSafari){
52728             this.updateHeaders();
52729         }
52730         this.updateSplitters();
52731         this.layout();
52732     },
52733
52734     unhideColumn : function(colIndex){
52735         var cid = this.getColumnId(colIndex);
52736         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
52737         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
52738
52739         if(Roo.isSafari){
52740             this.updateHeaders();
52741         }
52742         this.updateSplitters();
52743         this.layout();
52744     },
52745
52746     insertRows : function(dm, firstRow, lastRow, isUpdate){
52747         if(firstRow == 0 && lastRow == dm.getCount()-1){
52748             this.refresh();
52749         }else{
52750             if(!isUpdate){
52751                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
52752             }
52753             var s = this.getScrollState();
52754             var markup = this.renderRows(firstRow, lastRow);
52755             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
52756             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
52757             this.restoreScroll(s);
52758             if(!isUpdate){
52759                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
52760                 this.syncRowHeights(firstRow, lastRow);
52761                 this.stripeRows(firstRow);
52762                 this.layout();
52763             }
52764         }
52765     },
52766
52767     bufferRows : function(markup, target, index){
52768         var before = null, trows = target.rows, tbody = target.tBodies[0];
52769         if(index < trows.length){
52770             before = trows[index];
52771         }
52772         var b = document.createElement("div");
52773         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
52774         var rows = b.firstChild.rows;
52775         for(var i = 0, len = rows.length; i < len; i++){
52776             if(before){
52777                 tbody.insertBefore(rows[0], before);
52778             }else{
52779                 tbody.appendChild(rows[0]);
52780             }
52781         }
52782         b.innerHTML = "";
52783         b = null;
52784     },
52785
52786     deleteRows : function(dm, firstRow, lastRow){
52787         if(dm.getRowCount()<1){
52788             this.fireEvent("beforerefresh", this);
52789             this.mainBody.update("");
52790             this.lockedBody.update("");
52791             this.fireEvent("refresh", this);
52792         }else{
52793             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
52794             var bt = this.getBodyTable();
52795             var tbody = bt.firstChild;
52796             var rows = bt.rows;
52797             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
52798                 tbody.removeChild(rows[firstRow]);
52799             }
52800             this.stripeRows(firstRow);
52801             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
52802         }
52803     },
52804
52805     updateRows : function(dataSource, firstRow, lastRow){
52806         var s = this.getScrollState();
52807         this.refresh();
52808         this.restoreScroll(s);
52809     },
52810
52811     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
52812         if(!noRefresh){
52813            this.refresh();
52814         }
52815         this.updateHeaderSortState();
52816     },
52817
52818     getScrollState : function(){
52819         
52820         var sb = this.scroller.dom;
52821         return {left: sb.scrollLeft, top: sb.scrollTop};
52822     },
52823
52824     stripeRows : function(startRow){
52825         if(!this.grid.stripeRows || this.ds.getCount() < 1){
52826             return;
52827         }
52828         startRow = startRow || 0;
52829         var rows = this.getBodyTable().rows;
52830         var lrows = this.getLockedTable().rows;
52831         var cls = ' x-grid-row-alt ';
52832         for(var i = startRow, len = rows.length; i < len; i++){
52833             var row = rows[i], lrow = lrows[i];
52834             var isAlt = ((i+1) % 2 == 0);
52835             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
52836             if(isAlt == hasAlt){
52837                 continue;
52838             }
52839             if(isAlt){
52840                 row.className += " x-grid-row-alt";
52841             }else{
52842                 row.className = row.className.replace("x-grid-row-alt", "");
52843             }
52844             if(lrow){
52845                 lrow.className = row.className;
52846             }
52847         }
52848     },
52849
52850     restoreScroll : function(state){
52851         //Roo.log('GridView.restoreScroll');
52852         var sb = this.scroller.dom;
52853         sb.scrollLeft = state.left;
52854         sb.scrollTop = state.top;
52855         this.syncScroll();
52856     },
52857
52858     syncScroll : function(){
52859         //Roo.log('GridView.syncScroll');
52860         var sb = this.scroller.dom;
52861         var sh = this.mainHd.dom;
52862         var bs = this.mainBody.dom;
52863         var lv = this.lockedBody.dom;
52864         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
52865         lv.scrollTop = bs.scrollTop = sb.scrollTop;
52866     },
52867
52868     handleScroll : function(e){
52869         this.syncScroll();
52870         var sb = this.scroller.dom;
52871         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
52872         e.stopEvent();
52873     },
52874
52875     handleWheel : function(e){
52876         var d = e.getWheelDelta();
52877         this.scroller.dom.scrollTop -= d*22;
52878         // set this here to prevent jumpy scrolling on large tables
52879         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
52880         e.stopEvent();
52881     },
52882
52883     renderRows : function(startRow, endRow){
52884         // pull in all the crap needed to render rows
52885         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
52886         var colCount = cm.getColumnCount();
52887
52888         if(ds.getCount() < 1){
52889             return ["", ""];
52890         }
52891
52892         // build a map for all the columns
52893         var cs = [];
52894         for(var i = 0; i < colCount; i++){
52895             var name = cm.getDataIndex(i);
52896             cs[i] = {
52897                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
52898                 renderer : cm.getRenderer(i),
52899                 id : cm.getColumnId(i),
52900                 locked : cm.isLocked(i)
52901             };
52902         }
52903
52904         startRow = startRow || 0;
52905         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
52906
52907         // records to render
52908         var rs = ds.getRange(startRow, endRow);
52909
52910         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
52911     },
52912
52913     // As much as I hate to duplicate code, this was branched because FireFox really hates
52914     // [].join("") on strings. The performance difference was substantial enough to
52915     // branch this function
52916     doRender : Roo.isGecko ?
52917             function(cs, rs, ds, startRow, colCount, stripe){
52918                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52919                 // buffers
52920                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52921                 
52922                 var hasListener = this.grid.hasListener('rowclass');
52923                 var rowcfg = {};
52924                 for(var j = 0, len = rs.length; j < len; j++){
52925                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
52926                     for(var i = 0; i < colCount; i++){
52927                         c = cs[i];
52928                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52929                         p.id = c.id;
52930                         p.css = p.attr = "";
52931                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52932                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52933                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52934                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52935                         }
52936                         var markup = ct.apply(p);
52937                         if(!c.locked){
52938                             cb+= markup;
52939                         }else{
52940                             lcb+= markup;
52941                         }
52942                     }
52943                     var alt = [];
52944                     if(stripe && ((rowIndex+1) % 2 == 0)){
52945                         alt.push("x-grid-row-alt")
52946                     }
52947                     if(r.dirty){
52948                         alt.push(  " x-grid-dirty-row");
52949                     }
52950                     rp.cells = lcb;
52951                     if(this.getRowClass){
52952                         alt.push(this.getRowClass(r, rowIndex));
52953                     }
52954                     if (hasListener) {
52955                         rowcfg = {
52956                              
52957                             record: r,
52958                             rowIndex : rowIndex,
52959                             rowClass : ''
52960                         }
52961                         this.grid.fireEvent('rowclass', this, rowcfg);
52962                         alt.push(rowcfg.rowClass);
52963                     }
52964                     rp.alt = alt.join(" ");
52965                     lbuf+= rt.apply(rp);
52966                     rp.cells = cb;
52967                     buf+=  rt.apply(rp);
52968                 }
52969                 return [lbuf, buf];
52970             } :
52971             function(cs, rs, ds, startRow, colCount, stripe){
52972                 var ts = this.templates, ct = ts.cell, rt = ts.row;
52973                 // buffers
52974                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
52975                 var hasListener = this.grid.hasListener('rowclass');
52976  
52977                 var rowcfg = {};
52978                 for(var j = 0, len = rs.length; j < len; j++){
52979                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
52980                     for(var i = 0; i < colCount; i++){
52981                         c = cs[i];
52982                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
52983                         p.id = c.id;
52984                         p.css = p.attr = "";
52985                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
52986                         if(p.value == undefined || p.value === "") p.value = "&#160;";
52987                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
52988                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
52989                         }
52990                         
52991                         var markup = ct.apply(p);
52992                         if(!c.locked){
52993                             cb[cb.length] = markup;
52994                         }else{
52995                             lcb[lcb.length] = markup;
52996                         }
52997                     }
52998                     var alt = [];
52999                     if(stripe && ((rowIndex+1) % 2 == 0)){
53000                         alt.push( "x-grid-row-alt");
53001                     }
53002                     if(r.dirty){
53003                         alt.push(" x-grid-dirty-row");
53004                     }
53005                     rp.cells = lcb;
53006                     if(this.getRowClass){
53007                         alt.push( this.getRowClass(r, rowIndex));
53008                     }
53009                     if (hasListener) {
53010                         rowcfg = {
53011                              
53012                             record: r,
53013                             rowIndex : rowIndex,
53014                             rowClass : ''
53015                         }
53016                         this.grid.fireEvent('rowclass', this, rowcfg);
53017                         alt.push(rowcfg.rowClass);
53018                     }
53019                     rp.alt = alt.join(" ");
53020                     rp.cells = lcb.join("");
53021                     lbuf[lbuf.length] = rt.apply(rp);
53022                     rp.cells = cb.join("");
53023                     buf[buf.length] =  rt.apply(rp);
53024                 }
53025                 return [lbuf.join(""), buf.join("")];
53026             },
53027
53028     renderBody : function(){
53029         var markup = this.renderRows();
53030         var bt = this.templates.body;
53031         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53032     },
53033
53034     /**
53035      * Refreshes the grid
53036      * @param {Boolean} headersToo
53037      */
53038     refresh : function(headersToo){
53039         this.fireEvent("beforerefresh", this);
53040         this.grid.stopEditing();
53041         var result = this.renderBody();
53042         this.lockedBody.update(result[0]);
53043         this.mainBody.update(result[1]);
53044         if(headersToo === true){
53045             this.updateHeaders();
53046             this.updateColumns();
53047             this.updateSplitters();
53048             this.updateHeaderSortState();
53049         }
53050         this.syncRowHeights();
53051         this.layout();
53052         this.fireEvent("refresh", this);
53053     },
53054
53055     handleColumnMove : function(cm, oldIndex, newIndex){
53056         this.indexMap = null;
53057         var s = this.getScrollState();
53058         this.refresh(true);
53059         this.restoreScroll(s);
53060         this.afterMove(newIndex);
53061     },
53062
53063     afterMove : function(colIndex){
53064         if(this.enableMoveAnim && Roo.enableFx){
53065             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53066         }
53067         // if multisort - fix sortOrder, and reload..
53068         if (this.grid.dataSource.multiSort) {
53069             // the we can call sort again..
53070             var dm = this.grid.dataSource;
53071             var cm = this.grid.colModel;
53072             var so = [];
53073             for(var i = 0; i < cm.config.length; i++ ) {
53074                 
53075                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53076                     continue; // dont' bother, it's not in sort list or being set.
53077                 }
53078                 
53079                 so.push(cm.config[i].dataIndex);
53080             };
53081             dm.sortOrder = so;
53082             dm.load(dm.lastOptions);
53083             
53084             
53085         }
53086         
53087     },
53088
53089     updateCell : function(dm, rowIndex, dataIndex){
53090         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53091         if(typeof colIndex == "undefined"){ // not present in grid
53092             return;
53093         }
53094         var cm = this.grid.colModel;
53095         var cell = this.getCell(rowIndex, colIndex);
53096         var cellText = this.getCellText(rowIndex, colIndex);
53097
53098         var p = {
53099             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53100             id : cm.getColumnId(colIndex),
53101             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53102         };
53103         var renderer = cm.getRenderer(colIndex);
53104         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53105         if(typeof val == "undefined" || val === "") val = "&#160;";
53106         cellText.innerHTML = val;
53107         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53108         this.syncRowHeights(rowIndex, rowIndex);
53109     },
53110
53111     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53112         var maxWidth = 0;
53113         if(this.grid.autoSizeHeaders){
53114             var h = this.getHeaderCellMeasure(colIndex);
53115             maxWidth = Math.max(maxWidth, h.scrollWidth);
53116         }
53117         var tb, index;
53118         if(this.cm.isLocked(colIndex)){
53119             tb = this.getLockedTable();
53120             index = colIndex;
53121         }else{
53122             tb = this.getBodyTable();
53123             index = colIndex - this.cm.getLockedCount();
53124         }
53125         if(tb && tb.rows){
53126             var rows = tb.rows;
53127             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53128             for(var i = 0; i < stopIndex; i++){
53129                 var cell = rows[i].childNodes[index].firstChild;
53130                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53131             }
53132         }
53133         return maxWidth + /*margin for error in IE*/ 5;
53134     },
53135     /**
53136      * Autofit a column to its content.
53137      * @param {Number} colIndex
53138      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53139      */
53140      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53141          if(this.cm.isHidden(colIndex)){
53142              return; // can't calc a hidden column
53143          }
53144         if(forceMinSize){
53145             var cid = this.cm.getColumnId(colIndex);
53146             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53147            if(this.grid.autoSizeHeaders){
53148                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53149            }
53150         }
53151         var newWidth = this.calcColumnWidth(colIndex);
53152         this.cm.setColumnWidth(colIndex,
53153             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53154         if(!suppressEvent){
53155             this.grid.fireEvent("columnresize", colIndex, newWidth);
53156         }
53157     },
53158
53159     /**
53160      * Autofits all columns to their content and then expands to fit any extra space in the grid
53161      */
53162      autoSizeColumns : function(){
53163         var cm = this.grid.colModel;
53164         var colCount = cm.getColumnCount();
53165         for(var i = 0; i < colCount; i++){
53166             this.autoSizeColumn(i, true, true);
53167         }
53168         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53169             this.fitColumns();
53170         }else{
53171             this.updateColumns();
53172             this.layout();
53173         }
53174     },
53175
53176     /**
53177      * Autofits all columns to the grid's width proportionate with their current size
53178      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53179      */
53180     fitColumns : function(reserveScrollSpace){
53181         var cm = this.grid.colModel;
53182         var colCount = cm.getColumnCount();
53183         var cols = [];
53184         var width = 0;
53185         var i, w;
53186         for (i = 0; i < colCount; i++){
53187             if(!cm.isHidden(i) && !cm.isFixed(i)){
53188                 w = cm.getColumnWidth(i);
53189                 cols.push(i);
53190                 cols.push(w);
53191                 width += w;
53192             }
53193         }
53194         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53195         if(reserveScrollSpace){
53196             avail -= 17;
53197         }
53198         var frac = (avail - cm.getTotalWidth())/width;
53199         while (cols.length){
53200             w = cols.pop();
53201             i = cols.pop();
53202             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53203         }
53204         this.updateColumns();
53205         this.layout();
53206     },
53207
53208     onRowSelect : function(rowIndex){
53209         var row = this.getRowComposite(rowIndex);
53210         row.addClass("x-grid-row-selected");
53211     },
53212
53213     onRowDeselect : function(rowIndex){
53214         var row = this.getRowComposite(rowIndex);
53215         row.removeClass("x-grid-row-selected");
53216     },
53217
53218     onCellSelect : function(row, col){
53219         var cell = this.getCell(row, col);
53220         if(cell){
53221             Roo.fly(cell).addClass("x-grid-cell-selected");
53222         }
53223     },
53224
53225     onCellDeselect : function(row, col){
53226         var cell = this.getCell(row, col);
53227         if(cell){
53228             Roo.fly(cell).removeClass("x-grid-cell-selected");
53229         }
53230     },
53231
53232     updateHeaderSortState : function(){
53233         
53234         // sort state can be single { field: xxx, direction : yyy}
53235         // or   { xxx=>ASC , yyy : DESC ..... }
53236         
53237         var mstate = {};
53238         if (!this.ds.multiSort) { 
53239             var state = this.ds.getSortState();
53240             if(!state){
53241                 return;
53242             }
53243             mstate[state.field] = state.direction;
53244             // FIXME... - this is not used here.. but might be elsewhere..
53245             this.sortState = state;
53246             
53247         } else {
53248             mstate = this.ds.sortToggle;
53249         }
53250         //remove existing sort classes..
53251         
53252         var sc = this.sortClasses;
53253         var hds = this.el.select(this.headerSelector).removeClass(sc);
53254         
53255         for(var f in mstate) {
53256         
53257             var sortColumn = this.cm.findColumnIndex(f);
53258             
53259             if(sortColumn != -1){
53260                 var sortDir = mstate[f];        
53261                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53262             }
53263         }
53264         
53265          
53266         
53267     },
53268
53269
53270     handleHeaderClick : function(g, index){
53271         if(this.headersDisabled){
53272             return;
53273         }
53274         var dm = g.dataSource, cm = g.colModel;
53275         if(!cm.isSortable(index)){
53276             return;
53277         }
53278         g.stopEditing();
53279         
53280         if (dm.multiSort) {
53281             // update the sortOrder
53282             var so = [];
53283             for(var i = 0; i < cm.config.length; i++ ) {
53284                 
53285                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53286                     continue; // dont' bother, it's not in sort list or being set.
53287                 }
53288                 
53289                 so.push(cm.config[i].dataIndex);
53290             };
53291             dm.sortOrder = so;
53292         }
53293         
53294         
53295         dm.sort(cm.getDataIndex(index));
53296     },
53297
53298
53299     destroy : function(){
53300         if(this.colMenu){
53301             this.colMenu.removeAll();
53302             Roo.menu.MenuMgr.unregister(this.colMenu);
53303             this.colMenu.getEl().remove();
53304             delete this.colMenu;
53305         }
53306         if(this.hmenu){
53307             this.hmenu.removeAll();
53308             Roo.menu.MenuMgr.unregister(this.hmenu);
53309             this.hmenu.getEl().remove();
53310             delete this.hmenu;
53311         }
53312         if(this.grid.enableColumnMove){
53313             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53314             if(dds){
53315                 for(var dd in dds){
53316                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53317                         var elid = dds[dd].dragElId;
53318                         dds[dd].unreg();
53319                         Roo.get(elid).remove();
53320                     } else if(dds[dd].config.isTarget){
53321                         dds[dd].proxyTop.remove();
53322                         dds[dd].proxyBottom.remove();
53323                         dds[dd].unreg();
53324                     }
53325                     if(Roo.dd.DDM.locationCache[dd]){
53326                         delete Roo.dd.DDM.locationCache[dd];
53327                     }
53328                 }
53329                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53330             }
53331         }
53332         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53333         this.bind(null, null);
53334         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53335     },
53336
53337     handleLockChange : function(){
53338         this.refresh(true);
53339     },
53340
53341     onDenyColumnLock : function(){
53342
53343     },
53344
53345     onDenyColumnHide : function(){
53346
53347     },
53348
53349     handleHdMenuClick : function(item){
53350         var index = this.hdCtxIndex;
53351         var cm = this.cm, ds = this.ds;
53352         switch(item.id){
53353             case "asc":
53354                 ds.sort(cm.getDataIndex(index), "ASC");
53355                 break;
53356             case "desc":
53357                 ds.sort(cm.getDataIndex(index), "DESC");
53358                 break;
53359             case "lock":
53360                 var lc = cm.getLockedCount();
53361                 if(cm.getColumnCount(true) <= lc+1){
53362                     this.onDenyColumnLock();
53363                     return;
53364                 }
53365                 if(lc != index){
53366                     cm.setLocked(index, true, true);
53367                     cm.moveColumn(index, lc);
53368                     this.grid.fireEvent("columnmove", index, lc);
53369                 }else{
53370                     cm.setLocked(index, true);
53371                 }
53372             break;
53373             case "unlock":
53374                 var lc = cm.getLockedCount();
53375                 if((lc-1) != index){
53376                     cm.setLocked(index, false, true);
53377                     cm.moveColumn(index, lc-1);
53378                     this.grid.fireEvent("columnmove", index, lc-1);
53379                 }else{
53380                     cm.setLocked(index, false);
53381                 }
53382             break;
53383             default:
53384                 index = cm.getIndexById(item.id.substr(4));
53385                 if(index != -1){
53386                     if(item.checked && cm.getColumnCount(true) <= 1){
53387                         this.onDenyColumnHide();
53388                         return false;
53389                     }
53390                     cm.setHidden(index, item.checked);
53391                 }
53392         }
53393         return true;
53394     },
53395
53396     beforeColMenuShow : function(){
53397         var cm = this.cm,  colCount = cm.getColumnCount();
53398         this.colMenu.removeAll();
53399         for(var i = 0; i < colCount; i++){
53400             this.colMenu.add(new Roo.menu.CheckItem({
53401                 id: "col-"+cm.getColumnId(i),
53402                 text: cm.getColumnHeader(i),
53403                 checked: !cm.isHidden(i),
53404                 hideOnClick:false
53405             }));
53406         }
53407     },
53408
53409     handleHdCtx : function(g, index, e){
53410         e.stopEvent();
53411         var hd = this.getHeaderCell(index);
53412         this.hdCtxIndex = index;
53413         var ms = this.hmenu.items, cm = this.cm;
53414         ms.get("asc").setDisabled(!cm.isSortable(index));
53415         ms.get("desc").setDisabled(!cm.isSortable(index));
53416         if(this.grid.enableColLock !== false){
53417             ms.get("lock").setDisabled(cm.isLocked(index));
53418             ms.get("unlock").setDisabled(!cm.isLocked(index));
53419         }
53420         this.hmenu.show(hd, "tl-bl");
53421     },
53422
53423     handleHdOver : function(e){
53424         var hd = this.findHeaderCell(e.getTarget());
53425         if(hd && !this.headersDisabled){
53426             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53427                this.fly(hd).addClass("x-grid-hd-over");
53428             }
53429         }
53430     },
53431
53432     handleHdOut : function(e){
53433         var hd = this.findHeaderCell(e.getTarget());
53434         if(hd){
53435             this.fly(hd).removeClass("x-grid-hd-over");
53436         }
53437     },
53438
53439     handleSplitDblClick : function(e, t){
53440         var i = this.getCellIndex(t);
53441         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53442             this.autoSizeColumn(i, true);
53443             this.layout();
53444         }
53445     },
53446
53447     render : function(){
53448
53449         var cm = this.cm;
53450         var colCount = cm.getColumnCount();
53451
53452         if(this.grid.monitorWindowResize === true){
53453             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53454         }
53455         var header = this.renderHeaders();
53456         var body = this.templates.body.apply({rows:""});
53457         var html = this.templates.master.apply({
53458             lockedBody: body,
53459             body: body,
53460             lockedHeader: header[0],
53461             header: header[1]
53462         });
53463
53464         //this.updateColumns();
53465
53466         this.grid.getGridEl().dom.innerHTML = html;
53467
53468         this.initElements();
53469         
53470         // a kludge to fix the random scolling effect in webkit
53471         this.el.on("scroll", function() {
53472             this.el.dom.scrollTop=0; // hopefully not recursive..
53473         },this);
53474
53475         this.scroller.on("scroll", this.handleScroll, this);
53476         this.lockedBody.on("mousewheel", this.handleWheel, this);
53477         this.mainBody.on("mousewheel", this.handleWheel, this);
53478
53479         this.mainHd.on("mouseover", this.handleHdOver, this);
53480         this.mainHd.on("mouseout", this.handleHdOut, this);
53481         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53482                 {delegate: "."+this.splitClass});
53483
53484         this.lockedHd.on("mouseover", this.handleHdOver, this);
53485         this.lockedHd.on("mouseout", this.handleHdOut, this);
53486         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53487                 {delegate: "."+this.splitClass});
53488
53489         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53490             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53491         }
53492
53493         this.updateSplitters();
53494
53495         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53496             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53497             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53498         }
53499
53500         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53501             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53502             this.hmenu.add(
53503                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53504                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53505             );
53506             if(this.grid.enableColLock !== false){
53507                 this.hmenu.add('-',
53508                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53509                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53510                 );
53511             }
53512             if(this.grid.enableColumnHide !== false){
53513
53514                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53515                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53516                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53517
53518                 this.hmenu.add('-',
53519                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53520                 );
53521             }
53522             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53523
53524             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53525         }
53526
53527         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53528             this.dd = new Roo.grid.GridDragZone(this.grid, {
53529                 ddGroup : this.grid.ddGroup || 'GridDD'
53530             });
53531             
53532         }
53533
53534         /*
53535         for(var i = 0; i < colCount; i++){
53536             if(cm.isHidden(i)){
53537                 this.hideColumn(i);
53538             }
53539             if(cm.config[i].align){
53540                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53541                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53542             }
53543         }*/
53544         
53545         this.updateHeaderSortState();
53546
53547         this.beforeInitialResize();
53548         this.layout(true);
53549
53550         // two part rendering gives faster view to the user
53551         this.renderPhase2.defer(1, this);
53552     },
53553
53554     renderPhase2 : function(){
53555         // render the rows now
53556         this.refresh();
53557         if(this.grid.autoSizeColumns){
53558             this.autoSizeColumns();
53559         }
53560     },
53561
53562     beforeInitialResize : function(){
53563
53564     },
53565
53566     onColumnSplitterMoved : function(i, w){
53567         this.userResized = true;
53568         var cm = this.grid.colModel;
53569         cm.setColumnWidth(i, w, true);
53570         var cid = cm.getColumnId(i);
53571         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53572         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53573         this.updateSplitters();
53574         this.layout();
53575         this.grid.fireEvent("columnresize", i, w);
53576     },
53577
53578     syncRowHeights : function(startIndex, endIndex){
53579         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53580             startIndex = startIndex || 0;
53581             var mrows = this.getBodyTable().rows;
53582             var lrows = this.getLockedTable().rows;
53583             var len = mrows.length-1;
53584             endIndex = Math.min(endIndex || len, len);
53585             for(var i = startIndex; i <= endIndex; i++){
53586                 var m = mrows[i], l = lrows[i];
53587                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53588                 m.style.height = l.style.height = h + "px";
53589             }
53590         }
53591     },
53592
53593     layout : function(initialRender, is2ndPass){
53594         var g = this.grid;
53595         var auto = g.autoHeight;
53596         var scrollOffset = 16;
53597         var c = g.getGridEl(), cm = this.cm,
53598                 expandCol = g.autoExpandColumn,
53599                 gv = this;
53600         //c.beginMeasure();
53601
53602         if(!c.dom.offsetWidth){ // display:none?
53603             if(initialRender){
53604                 this.lockedWrap.show();
53605                 this.mainWrap.show();
53606             }
53607             return;
53608         }
53609
53610         var hasLock = this.cm.isLocked(0);
53611
53612         var tbh = this.headerPanel.getHeight();
53613         var bbh = this.footerPanel.getHeight();
53614
53615         if(auto){
53616             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53617             var newHeight = ch + c.getBorderWidth("tb");
53618             if(g.maxHeight){
53619                 newHeight = Math.min(g.maxHeight, newHeight);
53620             }
53621             c.setHeight(newHeight);
53622         }
53623
53624         if(g.autoWidth){
53625             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
53626         }
53627
53628         var s = this.scroller;
53629
53630         var csize = c.getSize(true);
53631
53632         this.el.setSize(csize.width, csize.height);
53633
53634         this.headerPanel.setWidth(csize.width);
53635         this.footerPanel.setWidth(csize.width);
53636
53637         var hdHeight = this.mainHd.getHeight();
53638         var vw = csize.width;
53639         var vh = csize.height - (tbh + bbh);
53640
53641         s.setSize(vw, vh);
53642
53643         var bt = this.getBodyTable();
53644         var ltWidth = hasLock ?
53645                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
53646
53647         var scrollHeight = bt.offsetHeight;
53648         var scrollWidth = ltWidth + bt.offsetWidth;
53649         var vscroll = false, hscroll = false;
53650
53651         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
53652
53653         var lw = this.lockedWrap, mw = this.mainWrap;
53654         var lb = this.lockedBody, mb = this.mainBody;
53655
53656         setTimeout(function(){
53657             var t = s.dom.offsetTop;
53658             var w = s.dom.clientWidth,
53659                 h = s.dom.clientHeight;
53660
53661             lw.setTop(t);
53662             lw.setSize(ltWidth, h);
53663
53664             mw.setLeftTop(ltWidth, t);
53665             mw.setSize(w-ltWidth, h);
53666
53667             lb.setHeight(h-hdHeight);
53668             mb.setHeight(h-hdHeight);
53669
53670             if(is2ndPass !== true && !gv.userResized && expandCol){
53671                 // high speed resize without full column calculation
53672                 
53673                 var ci = cm.getIndexById(expandCol);
53674                 if (ci < 0) {
53675                     ci = cm.findColumnIndex(expandCol);
53676                 }
53677                 ci = Math.max(0, ci); // make sure it's got at least the first col.
53678                 var expandId = cm.getColumnId(ci);
53679                 var  tw = cm.getTotalWidth(false);
53680                 var currentWidth = cm.getColumnWidth(ci);
53681                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
53682                 if(currentWidth != cw){
53683                     cm.setColumnWidth(ci, cw, true);
53684                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53685                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
53686                     gv.updateSplitters();
53687                     gv.layout(false, true);
53688                 }
53689             }
53690
53691             if(initialRender){
53692                 lw.show();
53693                 mw.show();
53694             }
53695             //c.endMeasure();
53696         }, 10);
53697     },
53698
53699     onWindowResize : function(){
53700         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
53701             return;
53702         }
53703         this.layout();
53704     },
53705
53706     appendFooter : function(parentEl){
53707         return null;
53708     },
53709
53710     sortAscText : "Sort Ascending",
53711     sortDescText : "Sort Descending",
53712     lockText : "Lock Column",
53713     unlockText : "Unlock Column",
53714     columnsText : "Columns"
53715 });
53716
53717
53718 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
53719     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
53720     this.proxy.el.addClass('x-grid3-col-dd');
53721 };
53722
53723 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
53724     handleMouseDown : function(e){
53725
53726     },
53727
53728     callHandleMouseDown : function(e){
53729         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
53730     }
53731 });
53732 /*
53733  * Based on:
53734  * Ext JS Library 1.1.1
53735  * Copyright(c) 2006-2007, Ext JS, LLC.
53736  *
53737  * Originally Released Under LGPL - original licence link has changed is not relivant.
53738  *
53739  * Fork - LGPL
53740  * <script type="text/javascript">
53741  */
53742  
53743 // private
53744 // This is a support class used internally by the Grid components
53745 Roo.grid.SplitDragZone = function(grid, hd, hd2){
53746     this.grid = grid;
53747     this.view = grid.getView();
53748     this.proxy = this.view.resizeProxy;
53749     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
53750         "gridSplitters" + this.grid.getGridEl().id, {
53751         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
53752     });
53753     this.setHandleElId(Roo.id(hd));
53754     this.setOuterHandleElId(Roo.id(hd2));
53755     this.scroll = false;
53756 };
53757 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
53758     fly: Roo.Element.fly,
53759
53760     b4StartDrag : function(x, y){
53761         this.view.headersDisabled = true;
53762         this.proxy.setHeight(this.view.mainWrap.getHeight());
53763         var w = this.cm.getColumnWidth(this.cellIndex);
53764         var minw = Math.max(w-this.grid.minColumnWidth, 0);
53765         this.resetConstraints();
53766         this.setXConstraint(minw, 1000);
53767         this.setYConstraint(0, 0);
53768         this.minX = x - minw;
53769         this.maxX = x + 1000;
53770         this.startPos = x;
53771         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
53772     },
53773
53774
53775     handleMouseDown : function(e){
53776         ev = Roo.EventObject.setEvent(e);
53777         var t = this.fly(ev.getTarget());
53778         if(t.hasClass("x-grid-split")){
53779             this.cellIndex = this.view.getCellIndex(t.dom);
53780             this.split = t.dom;
53781             this.cm = this.grid.colModel;
53782             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
53783                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
53784             }
53785         }
53786     },
53787
53788     endDrag : function(e){
53789         this.view.headersDisabled = false;
53790         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
53791         var diff = endX - this.startPos;
53792         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
53793     },
53794
53795     autoOffset : function(){
53796         this.setDelta(0,0);
53797     }
53798 });/*
53799  * Based on:
53800  * Ext JS Library 1.1.1
53801  * Copyright(c) 2006-2007, Ext JS, LLC.
53802  *
53803  * Originally Released Under LGPL - original licence link has changed is not relivant.
53804  *
53805  * Fork - LGPL
53806  * <script type="text/javascript">
53807  */
53808  
53809 // private
53810 // This is a support class used internally by the Grid components
53811 Roo.grid.GridDragZone = function(grid, config){
53812     this.view = grid.getView();
53813     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
53814     if(this.view.lockedBody){
53815         this.setHandleElId(Roo.id(this.view.mainBody.dom));
53816         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
53817     }
53818     this.scroll = false;
53819     this.grid = grid;
53820     this.ddel = document.createElement('div');
53821     this.ddel.className = 'x-grid-dd-wrap';
53822 };
53823
53824 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
53825     ddGroup : "GridDD",
53826
53827     getDragData : function(e){
53828         var t = Roo.lib.Event.getTarget(e);
53829         var rowIndex = this.view.findRowIndex(t);
53830         var sm = this.grid.selModel;
53831             
53832         //Roo.log(rowIndex);
53833         
53834         if (sm.getSelectedCell) {
53835             // cell selection..
53836             if (!sm.getSelectedCell()) {
53837                 return false;
53838             }
53839             if (rowIndex != sm.getSelectedCell()[0]) {
53840                 return false;
53841             }
53842         
53843         }
53844         
53845         if(rowIndex !== false){
53846             
53847             // if editorgrid.. 
53848             
53849             
53850             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
53851                
53852             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
53853               //  
53854             //}
53855             if (e.hasModifier()){
53856                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
53857             }
53858             
53859             Roo.log("getDragData");
53860             
53861             return {
53862                 grid: this.grid,
53863                 ddel: this.ddel,
53864                 rowIndex: rowIndex,
53865                 selections:sm.getSelections ? sm.getSelections() : (
53866                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
53867                 )
53868             };
53869         }
53870         return false;
53871     },
53872
53873     onInitDrag : function(e){
53874         var data = this.dragData;
53875         this.ddel.innerHTML = this.grid.getDragDropText();
53876         this.proxy.update(this.ddel);
53877         // fire start drag?
53878     },
53879
53880     afterRepair : function(){
53881         this.dragging = false;
53882     },
53883
53884     getRepairXY : function(e, data){
53885         return false;
53886     },
53887
53888     onEndDrag : function(data, e){
53889         // fire end drag?
53890     },
53891
53892     onValidDrop : function(dd, e, id){
53893         // fire drag drop?
53894         this.hideProxy();
53895     },
53896
53897     beforeInvalidDrop : function(e, id){
53898
53899     }
53900 });/*
53901  * Based on:
53902  * Ext JS Library 1.1.1
53903  * Copyright(c) 2006-2007, Ext JS, LLC.
53904  *
53905  * Originally Released Under LGPL - original licence link has changed is not relivant.
53906  *
53907  * Fork - LGPL
53908  * <script type="text/javascript">
53909  */
53910  
53911
53912 /**
53913  * @class Roo.grid.ColumnModel
53914  * @extends Roo.util.Observable
53915  * This is the default implementation of a ColumnModel used by the Grid. It defines
53916  * the columns in the grid.
53917  * <br>Usage:<br>
53918  <pre><code>
53919  var colModel = new Roo.grid.ColumnModel([
53920         {header: "Ticker", width: 60, sortable: true, locked: true},
53921         {header: "Company Name", width: 150, sortable: true},
53922         {header: "Market Cap.", width: 100, sortable: true},
53923         {header: "$ Sales", width: 100, sortable: true, renderer: money},
53924         {header: "Employees", width: 100, sortable: true, resizable: false}
53925  ]);
53926  </code></pre>
53927  * <p>
53928  
53929  * The config options listed for this class are options which may appear in each
53930  * individual column definition.
53931  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
53932  * @constructor
53933  * @param {Object} config An Array of column config objects. See this class's
53934  * config objects for details.
53935 */
53936 Roo.grid.ColumnModel = function(config){
53937         /**
53938      * The config passed into the constructor
53939      */
53940     this.config = config;
53941     this.lookup = {};
53942
53943     // if no id, create one
53944     // if the column does not have a dataIndex mapping,
53945     // map it to the order it is in the config
53946     for(var i = 0, len = config.length; i < len; i++){
53947         var c = config[i];
53948         if(typeof c.dataIndex == "undefined"){
53949             c.dataIndex = i;
53950         }
53951         if(typeof c.renderer == "string"){
53952             c.renderer = Roo.util.Format[c.renderer];
53953         }
53954         if(typeof c.id == "undefined"){
53955             c.id = Roo.id();
53956         }
53957         if(c.editor && c.editor.xtype){
53958             c.editor  = Roo.factory(c.editor, Roo.grid);
53959         }
53960         if(c.editor && c.editor.isFormField){
53961             c.editor = new Roo.grid.GridEditor(c.editor);
53962         }
53963         this.lookup[c.id] = c;
53964     }
53965
53966     /**
53967      * The width of columns which have no width specified (defaults to 100)
53968      * @type Number
53969      */
53970     this.defaultWidth = 100;
53971
53972     /**
53973      * Default sortable of columns which have no sortable specified (defaults to false)
53974      * @type Boolean
53975      */
53976     this.defaultSortable = false;
53977
53978     this.addEvents({
53979         /**
53980              * @event widthchange
53981              * Fires when the width of a column changes.
53982              * @param {ColumnModel} this
53983              * @param {Number} columnIndex The column index
53984              * @param {Number} newWidth The new width
53985              */
53986             "widthchange": true,
53987         /**
53988              * @event headerchange
53989              * Fires when the text of a header changes.
53990              * @param {ColumnModel} this
53991              * @param {Number} columnIndex The column index
53992              * @param {Number} newText The new header text
53993              */
53994             "headerchange": true,
53995         /**
53996              * @event hiddenchange
53997              * Fires when a column is hidden or "unhidden".
53998              * @param {ColumnModel} this
53999              * @param {Number} columnIndex The column index
54000              * @param {Boolean} hidden true if hidden, false otherwise
54001              */
54002             "hiddenchange": true,
54003             /**
54004          * @event columnmoved
54005          * Fires when a column is moved.
54006          * @param {ColumnModel} this
54007          * @param {Number} oldIndex
54008          * @param {Number} newIndex
54009          */
54010         "columnmoved" : true,
54011         /**
54012          * @event columlockchange
54013          * Fires when a column's locked state is changed
54014          * @param {ColumnModel} this
54015          * @param {Number} colIndex
54016          * @param {Boolean} locked true if locked
54017          */
54018         "columnlockchange" : true
54019     });
54020     Roo.grid.ColumnModel.superclass.constructor.call(this);
54021 };
54022 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54023     /**
54024      * @cfg {String} header The header text to display in the Grid view.
54025      */
54026     /**
54027      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54028      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54029      * specified, the column's index is used as an index into the Record's data Array.
54030      */
54031     /**
54032      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54033      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54034      */
54035     /**
54036      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54037      * Defaults to the value of the {@link #defaultSortable} property.
54038      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54039      */
54040     /**
54041      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54042      */
54043     /**
54044      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54045      */
54046     /**
54047      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54048      */
54049     /**
54050      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54051      */
54052     /**
54053      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54054      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54055      * default renderer uses the raw data value.
54056      */
54057        /**
54058      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54059      */
54060     /**
54061      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54062      */
54063
54064     /**
54065      * Returns the id of the column at the specified index.
54066      * @param {Number} index The column index
54067      * @return {String} the id
54068      */
54069     getColumnId : function(index){
54070         return this.config[index].id;
54071     },
54072
54073     /**
54074      * Returns the column for a specified id.
54075      * @param {String} id The column id
54076      * @return {Object} the column
54077      */
54078     getColumnById : function(id){
54079         return this.lookup[id];
54080     },
54081
54082     
54083     /**
54084      * Returns the column for a specified dataIndex.
54085      * @param {String} dataIndex The column dataIndex
54086      * @return {Object|Boolean} the column or false if not found
54087      */
54088     getColumnByDataIndex: function(dataIndex){
54089         var index = this.findColumnIndex(dataIndex);
54090         return index > -1 ? this.config[index] : false;
54091     },
54092     
54093     /**
54094      * Returns the index for a specified column id.
54095      * @param {String} id The column id
54096      * @return {Number} the index, or -1 if not found
54097      */
54098     getIndexById : function(id){
54099         for(var i = 0, len = this.config.length; i < len; i++){
54100             if(this.config[i].id == id){
54101                 return i;
54102             }
54103         }
54104         return -1;
54105     },
54106     
54107     /**
54108      * Returns the index for a specified column dataIndex.
54109      * @param {String} dataIndex The column dataIndex
54110      * @return {Number} the index, or -1 if not found
54111      */
54112     
54113     findColumnIndex : function(dataIndex){
54114         for(var i = 0, len = this.config.length; i < len; i++){
54115             if(this.config[i].dataIndex == dataIndex){
54116                 return i;
54117             }
54118         }
54119         return -1;
54120     },
54121     
54122     
54123     moveColumn : function(oldIndex, newIndex){
54124         var c = this.config[oldIndex];
54125         this.config.splice(oldIndex, 1);
54126         this.config.splice(newIndex, 0, c);
54127         this.dataMap = null;
54128         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54129     },
54130
54131     isLocked : function(colIndex){
54132         return this.config[colIndex].locked === true;
54133     },
54134
54135     setLocked : function(colIndex, value, suppressEvent){
54136         if(this.isLocked(colIndex) == value){
54137             return;
54138         }
54139         this.config[colIndex].locked = value;
54140         if(!suppressEvent){
54141             this.fireEvent("columnlockchange", this, colIndex, value);
54142         }
54143     },
54144
54145     getTotalLockedWidth : function(){
54146         var totalWidth = 0;
54147         for(var i = 0; i < this.config.length; i++){
54148             if(this.isLocked(i) && !this.isHidden(i)){
54149                 this.totalWidth += this.getColumnWidth(i);
54150             }
54151         }
54152         return totalWidth;
54153     },
54154
54155     getLockedCount : function(){
54156         for(var i = 0, len = this.config.length; i < len; i++){
54157             if(!this.isLocked(i)){
54158                 return i;
54159             }
54160         }
54161     },
54162
54163     /**
54164      * Returns the number of columns.
54165      * @return {Number}
54166      */
54167     getColumnCount : function(visibleOnly){
54168         if(visibleOnly === true){
54169             var c = 0;
54170             for(var i = 0, len = this.config.length; i < len; i++){
54171                 if(!this.isHidden(i)){
54172                     c++;
54173                 }
54174             }
54175             return c;
54176         }
54177         return this.config.length;
54178     },
54179
54180     /**
54181      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54182      * @param {Function} fn
54183      * @param {Object} scope (optional)
54184      * @return {Array} result
54185      */
54186     getColumnsBy : function(fn, scope){
54187         var r = [];
54188         for(var i = 0, len = this.config.length; i < len; i++){
54189             var c = this.config[i];
54190             if(fn.call(scope||this, c, i) === true){
54191                 r[r.length] = c;
54192             }
54193         }
54194         return r;
54195     },
54196
54197     /**
54198      * Returns true if the specified column is sortable.
54199      * @param {Number} col The column index
54200      * @return {Boolean}
54201      */
54202     isSortable : function(col){
54203         if(typeof this.config[col].sortable == "undefined"){
54204             return this.defaultSortable;
54205         }
54206         return this.config[col].sortable;
54207     },
54208
54209     /**
54210      * Returns the rendering (formatting) function defined for the column.
54211      * @param {Number} col The column index.
54212      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54213      */
54214     getRenderer : function(col){
54215         if(!this.config[col].renderer){
54216             return Roo.grid.ColumnModel.defaultRenderer;
54217         }
54218         return this.config[col].renderer;
54219     },
54220
54221     /**
54222      * Sets the rendering (formatting) function for a column.
54223      * @param {Number} col The column index
54224      * @param {Function} fn The function to use to process the cell's raw data
54225      * to return HTML markup for the grid view. The render function is called with
54226      * the following parameters:<ul>
54227      * <li>Data value.</li>
54228      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54229      * <li>css A CSS style string to apply to the table cell.</li>
54230      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54231      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54232      * <li>Row index</li>
54233      * <li>Column index</li>
54234      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54235      */
54236     setRenderer : function(col, fn){
54237         this.config[col].renderer = fn;
54238     },
54239
54240     /**
54241      * Returns the width for the specified column.
54242      * @param {Number} col The column index
54243      * @return {Number}
54244      */
54245     getColumnWidth : function(col){
54246         return this.config[col].width * 1 || this.defaultWidth;
54247     },
54248
54249     /**
54250      * Sets the width for a column.
54251      * @param {Number} col The column index
54252      * @param {Number} width The new width
54253      */
54254     setColumnWidth : function(col, width, suppressEvent){
54255         this.config[col].width = width;
54256         this.totalWidth = null;
54257         if(!suppressEvent){
54258              this.fireEvent("widthchange", this, col, width);
54259         }
54260     },
54261
54262     /**
54263      * Returns the total width of all columns.
54264      * @param {Boolean} includeHidden True to include hidden column widths
54265      * @return {Number}
54266      */
54267     getTotalWidth : function(includeHidden){
54268         if(!this.totalWidth){
54269             this.totalWidth = 0;
54270             for(var i = 0, len = this.config.length; i < len; i++){
54271                 if(includeHidden || !this.isHidden(i)){
54272                     this.totalWidth += this.getColumnWidth(i);
54273                 }
54274             }
54275         }
54276         return this.totalWidth;
54277     },
54278
54279     /**
54280      * Returns the header for the specified column.
54281      * @param {Number} col The column index
54282      * @return {String}
54283      */
54284     getColumnHeader : function(col){
54285         return this.config[col].header;
54286     },
54287
54288     /**
54289      * Sets the header for a column.
54290      * @param {Number} col The column index
54291      * @param {String} header The new header
54292      */
54293     setColumnHeader : function(col, header){
54294         this.config[col].header = header;
54295         this.fireEvent("headerchange", this, col, header);
54296     },
54297
54298     /**
54299      * Returns the tooltip for the specified column.
54300      * @param {Number} col The column index
54301      * @return {String}
54302      */
54303     getColumnTooltip : function(col){
54304             return this.config[col].tooltip;
54305     },
54306     /**
54307      * Sets the tooltip for a column.
54308      * @param {Number} col The column index
54309      * @param {String} tooltip The new tooltip
54310      */
54311     setColumnTooltip : function(col, tooltip){
54312             this.config[col].tooltip = tooltip;
54313     },
54314
54315     /**
54316      * Returns the dataIndex for the specified column.
54317      * @param {Number} col The column index
54318      * @return {Number}
54319      */
54320     getDataIndex : function(col){
54321         return this.config[col].dataIndex;
54322     },
54323
54324     /**
54325      * Sets the dataIndex for a column.
54326      * @param {Number} col The column index
54327      * @param {Number} dataIndex The new dataIndex
54328      */
54329     setDataIndex : function(col, dataIndex){
54330         this.config[col].dataIndex = dataIndex;
54331     },
54332
54333     
54334     
54335     /**
54336      * Returns true if the cell is editable.
54337      * @param {Number} colIndex The column index
54338      * @param {Number} rowIndex The row index
54339      * @return {Boolean}
54340      */
54341     isCellEditable : function(colIndex, rowIndex){
54342         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54343     },
54344
54345     /**
54346      * Returns the editor defined for the cell/column.
54347      * return false or null to disable editing.
54348      * @param {Number} colIndex The column index
54349      * @param {Number} rowIndex The row index
54350      * @return {Object}
54351      */
54352     getCellEditor : function(colIndex, rowIndex){
54353         return this.config[colIndex].editor;
54354     },
54355
54356     /**
54357      * Sets if a column is editable.
54358      * @param {Number} col The column index
54359      * @param {Boolean} editable True if the column is editable
54360      */
54361     setEditable : function(col, editable){
54362         this.config[col].editable = editable;
54363     },
54364
54365
54366     /**
54367      * Returns true if the column is hidden.
54368      * @param {Number} colIndex The column index
54369      * @return {Boolean}
54370      */
54371     isHidden : function(colIndex){
54372         return this.config[colIndex].hidden;
54373     },
54374
54375
54376     /**
54377      * Returns true if the column width cannot be changed
54378      */
54379     isFixed : function(colIndex){
54380         return this.config[colIndex].fixed;
54381     },
54382
54383     /**
54384      * Returns true if the column can be resized
54385      * @return {Boolean}
54386      */
54387     isResizable : function(colIndex){
54388         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54389     },
54390     /**
54391      * Sets if a column is hidden.
54392      * @param {Number} colIndex The column index
54393      * @param {Boolean} hidden True if the column is hidden
54394      */
54395     setHidden : function(colIndex, hidden){
54396         this.config[colIndex].hidden = hidden;
54397         this.totalWidth = null;
54398         this.fireEvent("hiddenchange", this, colIndex, hidden);
54399     },
54400
54401     /**
54402      * Sets the editor for a column.
54403      * @param {Number} col The column index
54404      * @param {Object} editor The editor object
54405      */
54406     setEditor : function(col, editor){
54407         this.config[col].editor = editor;
54408     }
54409 });
54410
54411 Roo.grid.ColumnModel.defaultRenderer = function(value){
54412         if(typeof value == "string" && value.length < 1){
54413             return "&#160;";
54414         }
54415         return value;
54416 };
54417
54418 // Alias for backwards compatibility
54419 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54420 /*
54421  * Based on:
54422  * Ext JS Library 1.1.1
54423  * Copyright(c) 2006-2007, Ext JS, LLC.
54424  *
54425  * Originally Released Under LGPL - original licence link has changed is not relivant.
54426  *
54427  * Fork - LGPL
54428  * <script type="text/javascript">
54429  */
54430
54431 /**
54432  * @class Roo.grid.AbstractSelectionModel
54433  * @extends Roo.util.Observable
54434  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54435  * implemented by descendant classes.  This class should not be directly instantiated.
54436  * @constructor
54437  */
54438 Roo.grid.AbstractSelectionModel = function(){
54439     this.locked = false;
54440     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54441 };
54442
54443 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54444     /** @ignore Called by the grid automatically. Do not call directly. */
54445     init : function(grid){
54446         this.grid = grid;
54447         this.initEvents();
54448     },
54449
54450     /**
54451      * Locks the selections.
54452      */
54453     lock : function(){
54454         this.locked = true;
54455     },
54456
54457     /**
54458      * Unlocks the selections.
54459      */
54460     unlock : function(){
54461         this.locked = false;
54462     },
54463
54464     /**
54465      * Returns true if the selections are locked.
54466      * @return {Boolean}
54467      */
54468     isLocked : function(){
54469         return this.locked;
54470     }
54471 });/*
54472  * Based on:
54473  * Ext JS Library 1.1.1
54474  * Copyright(c) 2006-2007, Ext JS, LLC.
54475  *
54476  * Originally Released Under LGPL - original licence link has changed is not relivant.
54477  *
54478  * Fork - LGPL
54479  * <script type="text/javascript">
54480  */
54481 /**
54482  * @extends Roo.grid.AbstractSelectionModel
54483  * @class Roo.grid.RowSelectionModel
54484  * The default SelectionModel used by {@link Roo.grid.Grid}.
54485  * It supports multiple selections and keyboard selection/navigation. 
54486  * @constructor
54487  * @param {Object} config
54488  */
54489 Roo.grid.RowSelectionModel = function(config){
54490     Roo.apply(this, config);
54491     this.selections = new Roo.util.MixedCollection(false, function(o){
54492         return o.id;
54493     });
54494
54495     this.last = false;
54496     this.lastActive = false;
54497
54498     this.addEvents({
54499         /**
54500              * @event selectionchange
54501              * Fires when the selection changes
54502              * @param {SelectionModel} this
54503              */
54504             "selectionchange" : true,
54505         /**
54506              * @event afterselectionchange
54507              * Fires after the selection changes (eg. by key press or clicking)
54508              * @param {SelectionModel} this
54509              */
54510             "afterselectionchange" : true,
54511         /**
54512              * @event beforerowselect
54513              * Fires when a row is selected being selected, return false to cancel.
54514              * @param {SelectionModel} this
54515              * @param {Number} rowIndex The selected index
54516              * @param {Boolean} keepExisting False if other selections will be cleared
54517              */
54518             "beforerowselect" : true,
54519         /**
54520              * @event rowselect
54521              * Fires when a row is selected.
54522              * @param {SelectionModel} this
54523              * @param {Number} rowIndex The selected index
54524              * @param {Roo.data.Record} r The record
54525              */
54526             "rowselect" : true,
54527         /**
54528              * @event rowdeselect
54529              * Fires when a row is deselected.
54530              * @param {SelectionModel} this
54531              * @param {Number} rowIndex The selected index
54532              */
54533         "rowdeselect" : true
54534     });
54535     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54536     this.locked = false;
54537 };
54538
54539 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54540     /**
54541      * @cfg {Boolean} singleSelect
54542      * True to allow selection of only one row at a time (defaults to false)
54543      */
54544     singleSelect : false,
54545
54546     // private
54547     initEvents : function(){
54548
54549         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54550             this.grid.on("mousedown", this.handleMouseDown, this);
54551         }else{ // allow click to work like normal
54552             this.grid.on("rowclick", this.handleDragableRowClick, this);
54553         }
54554
54555         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54556             "up" : function(e){
54557                 if(!e.shiftKey){
54558                     this.selectPrevious(e.shiftKey);
54559                 }else if(this.last !== false && this.lastActive !== false){
54560                     var last = this.last;
54561                     this.selectRange(this.last,  this.lastActive-1);
54562                     this.grid.getView().focusRow(this.lastActive);
54563                     if(last !== false){
54564                         this.last = last;
54565                     }
54566                 }else{
54567                     this.selectFirstRow();
54568                 }
54569                 this.fireEvent("afterselectionchange", this);
54570             },
54571             "down" : function(e){
54572                 if(!e.shiftKey){
54573                     this.selectNext(e.shiftKey);
54574                 }else if(this.last !== false && this.lastActive !== false){
54575                     var last = this.last;
54576                     this.selectRange(this.last,  this.lastActive+1);
54577                     this.grid.getView().focusRow(this.lastActive);
54578                     if(last !== false){
54579                         this.last = last;
54580                     }
54581                 }else{
54582                     this.selectFirstRow();
54583                 }
54584                 this.fireEvent("afterselectionchange", this);
54585             },
54586             scope: this
54587         });
54588
54589         var view = this.grid.view;
54590         view.on("refresh", this.onRefresh, this);
54591         view.on("rowupdated", this.onRowUpdated, this);
54592         view.on("rowremoved", this.onRemove, this);
54593     },
54594
54595     // private
54596     onRefresh : function(){
54597         var ds = this.grid.dataSource, i, v = this.grid.view;
54598         var s = this.selections;
54599         s.each(function(r){
54600             if((i = ds.indexOfId(r.id)) != -1){
54601                 v.onRowSelect(i);
54602             }else{
54603                 s.remove(r);
54604             }
54605         });
54606     },
54607
54608     // private
54609     onRemove : function(v, index, r){
54610         this.selections.remove(r);
54611     },
54612
54613     // private
54614     onRowUpdated : function(v, index, r){
54615         if(this.isSelected(r)){
54616             v.onRowSelect(index);
54617         }
54618     },
54619
54620     /**
54621      * Select records.
54622      * @param {Array} records The records to select
54623      * @param {Boolean} keepExisting (optional) True to keep existing selections
54624      */
54625     selectRecords : function(records, keepExisting){
54626         if(!keepExisting){
54627             this.clearSelections();
54628         }
54629         var ds = this.grid.dataSource;
54630         for(var i = 0, len = records.length; i < len; i++){
54631             this.selectRow(ds.indexOf(records[i]), true);
54632         }
54633     },
54634
54635     /**
54636      * Gets the number of selected rows.
54637      * @return {Number}
54638      */
54639     getCount : function(){
54640         return this.selections.length;
54641     },
54642
54643     /**
54644      * Selects the first row in the grid.
54645      */
54646     selectFirstRow : function(){
54647         this.selectRow(0);
54648     },
54649
54650     /**
54651      * Select the last row.
54652      * @param {Boolean} keepExisting (optional) True to keep existing selections
54653      */
54654     selectLastRow : function(keepExisting){
54655         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
54656     },
54657
54658     /**
54659      * Selects the row immediately following the last selected row.
54660      * @param {Boolean} keepExisting (optional) True to keep existing selections
54661      */
54662     selectNext : function(keepExisting){
54663         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
54664             this.selectRow(this.last+1, keepExisting);
54665             this.grid.getView().focusRow(this.last);
54666         }
54667     },
54668
54669     /**
54670      * Selects the row that precedes the last selected row.
54671      * @param {Boolean} keepExisting (optional) True to keep existing selections
54672      */
54673     selectPrevious : function(keepExisting){
54674         if(this.last){
54675             this.selectRow(this.last-1, keepExisting);
54676             this.grid.getView().focusRow(this.last);
54677         }
54678     },
54679
54680     /**
54681      * Returns the selected records
54682      * @return {Array} Array of selected records
54683      */
54684     getSelections : function(){
54685         return [].concat(this.selections.items);
54686     },
54687
54688     /**
54689      * Returns the first selected record.
54690      * @return {Record}
54691      */
54692     getSelected : function(){
54693         return this.selections.itemAt(0);
54694     },
54695
54696
54697     /**
54698      * Clears all selections.
54699      */
54700     clearSelections : function(fast){
54701         if(this.locked) return;
54702         if(fast !== true){
54703             var ds = this.grid.dataSource;
54704             var s = this.selections;
54705             s.each(function(r){
54706                 this.deselectRow(ds.indexOfId(r.id));
54707             }, this);
54708             s.clear();
54709         }else{
54710             this.selections.clear();
54711         }
54712         this.last = false;
54713     },
54714
54715
54716     /**
54717      * Selects all rows.
54718      */
54719     selectAll : function(){
54720         if(this.locked) return;
54721         this.selections.clear();
54722         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
54723             this.selectRow(i, true);
54724         }
54725     },
54726
54727     /**
54728      * Returns True if there is a selection.
54729      * @return {Boolean}
54730      */
54731     hasSelection : function(){
54732         return this.selections.length > 0;
54733     },
54734
54735     /**
54736      * Returns True if the specified row is selected.
54737      * @param {Number/Record} record The record or index of the record to check
54738      * @return {Boolean}
54739      */
54740     isSelected : function(index){
54741         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
54742         return (r && this.selections.key(r.id) ? true : false);
54743     },
54744
54745     /**
54746      * Returns True if the specified record id is selected.
54747      * @param {String} id The id of record to check
54748      * @return {Boolean}
54749      */
54750     isIdSelected : function(id){
54751         return (this.selections.key(id) ? true : false);
54752     },
54753
54754     // private
54755     handleMouseDown : function(e, t){
54756         var view = this.grid.getView(), rowIndex;
54757         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
54758             return;
54759         };
54760         if(e.shiftKey && this.last !== false){
54761             var last = this.last;
54762             this.selectRange(last, rowIndex, e.ctrlKey);
54763             this.last = last; // reset the last
54764             view.focusRow(rowIndex);
54765         }else{
54766             var isSelected = this.isSelected(rowIndex);
54767             if(e.button !== 0 && isSelected){
54768                 view.focusRow(rowIndex);
54769             }else if(e.ctrlKey && isSelected){
54770                 this.deselectRow(rowIndex);
54771             }else if(!isSelected){
54772                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
54773                 view.focusRow(rowIndex);
54774             }
54775         }
54776         this.fireEvent("afterselectionchange", this);
54777     },
54778     // private
54779     handleDragableRowClick :  function(grid, rowIndex, e) 
54780     {
54781         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
54782             this.selectRow(rowIndex, false);
54783             grid.view.focusRow(rowIndex);
54784              this.fireEvent("afterselectionchange", this);
54785         }
54786     },
54787     
54788     /**
54789      * Selects multiple rows.
54790      * @param {Array} rows Array of the indexes of the row to select
54791      * @param {Boolean} keepExisting (optional) True to keep existing selections
54792      */
54793     selectRows : function(rows, keepExisting){
54794         if(!keepExisting){
54795             this.clearSelections();
54796         }
54797         for(var i = 0, len = rows.length; i < len; i++){
54798             this.selectRow(rows[i], true);
54799         }
54800     },
54801
54802     /**
54803      * Selects a range of rows. All rows in between startRow and endRow are also selected.
54804      * @param {Number} startRow The index of the first row in the range
54805      * @param {Number} endRow The index of the last row in the range
54806      * @param {Boolean} keepExisting (optional) True to retain existing selections
54807      */
54808     selectRange : function(startRow, endRow, keepExisting){
54809         if(this.locked) return;
54810         if(!keepExisting){
54811             this.clearSelections();
54812         }
54813         if(startRow <= endRow){
54814             for(var i = startRow; i <= endRow; i++){
54815                 this.selectRow(i, true);
54816             }
54817         }else{
54818             for(var i = startRow; i >= endRow; i--){
54819                 this.selectRow(i, true);
54820             }
54821         }
54822     },
54823
54824     /**
54825      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
54826      * @param {Number} startRow The index of the first row in the range
54827      * @param {Number} endRow The index of the last row in the range
54828      */
54829     deselectRange : function(startRow, endRow, preventViewNotify){
54830         if(this.locked) return;
54831         for(var i = startRow; i <= endRow; i++){
54832             this.deselectRow(i, preventViewNotify);
54833         }
54834     },
54835
54836     /**
54837      * Selects a row.
54838      * @param {Number} row The index of the row to select
54839      * @param {Boolean} keepExisting (optional) True to keep existing selections
54840      */
54841     selectRow : function(index, keepExisting, preventViewNotify){
54842         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
54843         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
54844             if(!keepExisting || this.singleSelect){
54845                 this.clearSelections();
54846             }
54847             var r = this.grid.dataSource.getAt(index);
54848             this.selections.add(r);
54849             this.last = this.lastActive = index;
54850             if(!preventViewNotify){
54851                 this.grid.getView().onRowSelect(index);
54852             }
54853             this.fireEvent("rowselect", this, index, r);
54854             this.fireEvent("selectionchange", this);
54855         }
54856     },
54857
54858     /**
54859      * Deselects a row.
54860      * @param {Number} row The index of the row to deselect
54861      */
54862     deselectRow : function(index, preventViewNotify){
54863         if(this.locked) return;
54864         if(this.last == index){
54865             this.last = false;
54866         }
54867         if(this.lastActive == index){
54868             this.lastActive = false;
54869         }
54870         var r = this.grid.dataSource.getAt(index);
54871         this.selections.remove(r);
54872         if(!preventViewNotify){
54873             this.grid.getView().onRowDeselect(index);
54874         }
54875         this.fireEvent("rowdeselect", this, index);
54876         this.fireEvent("selectionchange", this);
54877     },
54878
54879     // private
54880     restoreLast : function(){
54881         if(this._last){
54882             this.last = this._last;
54883         }
54884     },
54885
54886     // private
54887     acceptsNav : function(row, col, cm){
54888         return !cm.isHidden(col) && cm.isCellEditable(col, row);
54889     },
54890
54891     // private
54892     onEditorKey : function(field, e){
54893         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
54894         if(k == e.TAB){
54895             e.stopEvent();
54896             ed.completeEdit();
54897             if(e.shiftKey){
54898                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
54899             }else{
54900                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
54901             }
54902         }else if(k == e.ENTER && !e.ctrlKey){
54903             e.stopEvent();
54904             ed.completeEdit();
54905             if(e.shiftKey){
54906                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
54907             }else{
54908                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
54909             }
54910         }else if(k == e.ESC){
54911             ed.cancelEdit();
54912         }
54913         if(newCell){
54914             g.startEditing(newCell[0], newCell[1]);
54915         }
54916     }
54917 });/*
54918  * Based on:
54919  * Ext JS Library 1.1.1
54920  * Copyright(c) 2006-2007, Ext JS, LLC.
54921  *
54922  * Originally Released Under LGPL - original licence link has changed is not relivant.
54923  *
54924  * Fork - LGPL
54925  * <script type="text/javascript">
54926  */
54927 /**
54928  * @class Roo.grid.CellSelectionModel
54929  * @extends Roo.grid.AbstractSelectionModel
54930  * This class provides the basic implementation for cell selection in a grid.
54931  * @constructor
54932  * @param {Object} config The object containing the configuration of this model.
54933  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
54934  */
54935 Roo.grid.CellSelectionModel = function(config){
54936     Roo.apply(this, config);
54937
54938     this.selection = null;
54939
54940     this.addEvents({
54941         /**
54942              * @event beforerowselect
54943              * Fires before a cell is selected.
54944              * @param {SelectionModel} this
54945              * @param {Number} rowIndex The selected row index
54946              * @param {Number} colIndex The selected cell index
54947              */
54948             "beforecellselect" : true,
54949         /**
54950              * @event cellselect
54951              * Fires when a cell is selected.
54952              * @param {SelectionModel} this
54953              * @param {Number} rowIndex The selected row index
54954              * @param {Number} colIndex The selected cell index
54955              */
54956             "cellselect" : true,
54957         /**
54958              * @event selectionchange
54959              * Fires when the active selection changes.
54960              * @param {SelectionModel} this
54961              * @param {Object} selection null for no selection or an object (o) with two properties
54962                 <ul>
54963                 <li>o.record: the record object for the row the selection is in</li>
54964                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
54965                 </ul>
54966              */
54967             "selectionchange" : true,
54968         /**
54969              * @event tabend
54970              * Fires when the tab (or enter) was pressed on the last editable cell
54971              * You can use this to trigger add new row.
54972              * @param {SelectionModel} this
54973              */
54974             "tabend" : true,
54975          /**
54976              * @event beforeeditnext
54977              * Fires before the next editable sell is made active
54978              * You can use this to skip to another cell or fire the tabend
54979              *    if you set cell to false
54980              * @param {Object} eventdata object : { cell : [ row, col ] } 
54981              */
54982             "beforeeditnext" : true
54983     });
54984     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54985 };
54986
54987 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
54988     
54989     enter_is_tab: false,
54990
54991     /** @ignore */
54992     initEvents : function(){
54993         this.grid.on("mousedown", this.handleMouseDown, this);
54994         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
54995         var view = this.grid.view;
54996         view.on("refresh", this.onViewChange, this);
54997         view.on("rowupdated", this.onRowUpdated, this);
54998         view.on("beforerowremoved", this.clearSelections, this);
54999         view.on("beforerowsinserted", this.clearSelections, this);
55000         if(this.grid.isEditor){
55001             this.grid.on("beforeedit", this.beforeEdit,  this);
55002         }
55003     },
55004
55005         //private
55006     beforeEdit : function(e){
55007         this.select(e.row, e.column, false, true, e.record);
55008     },
55009
55010         //private
55011     onRowUpdated : function(v, index, r){
55012         if(this.selection && this.selection.record == r){
55013             v.onCellSelect(index, this.selection.cell[1]);
55014         }
55015     },
55016
55017         //private
55018     onViewChange : function(){
55019         this.clearSelections(true);
55020     },
55021
55022         /**
55023          * Returns the currently selected cell,.
55024          * @return {Array} The selected cell (row, column) or null if none selected.
55025          */
55026     getSelectedCell : function(){
55027         return this.selection ? this.selection.cell : null;
55028     },
55029
55030     /**
55031      * Clears all selections.
55032      * @param {Boolean} true to prevent the gridview from being notified about the change.
55033      */
55034     clearSelections : function(preventNotify){
55035         var s = this.selection;
55036         if(s){
55037             if(preventNotify !== true){
55038                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55039             }
55040             this.selection = null;
55041             this.fireEvent("selectionchange", this, null);
55042         }
55043     },
55044
55045     /**
55046      * Returns true if there is a selection.
55047      * @return {Boolean}
55048      */
55049     hasSelection : function(){
55050         return this.selection ? true : false;
55051     },
55052
55053     /** @ignore */
55054     handleMouseDown : function(e, t){
55055         var v = this.grid.getView();
55056         if(this.isLocked()){
55057             return;
55058         };
55059         var row = v.findRowIndex(t);
55060         var cell = v.findCellIndex(t);
55061         if(row !== false && cell !== false){
55062             this.select(row, cell);
55063         }
55064     },
55065
55066     /**
55067      * Selects a cell.
55068      * @param {Number} rowIndex
55069      * @param {Number} collIndex
55070      */
55071     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55072         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55073             this.clearSelections();
55074             r = r || this.grid.dataSource.getAt(rowIndex);
55075             this.selection = {
55076                 record : r,
55077                 cell : [rowIndex, colIndex]
55078             };
55079             if(!preventViewNotify){
55080                 var v = this.grid.getView();
55081                 v.onCellSelect(rowIndex, colIndex);
55082                 if(preventFocus !== true){
55083                     v.focusCell(rowIndex, colIndex);
55084                 }
55085             }
55086             this.fireEvent("cellselect", this, rowIndex, colIndex);
55087             this.fireEvent("selectionchange", this, this.selection);
55088         }
55089     },
55090
55091         //private
55092     isSelectable : function(rowIndex, colIndex, cm){
55093         return !cm.isHidden(colIndex);
55094     },
55095
55096     /** @ignore */
55097     handleKeyDown : function(e){
55098         //Roo.log('Cell Sel Model handleKeyDown');
55099         if(!e.isNavKeyPress()){
55100             return;
55101         }
55102         var g = this.grid, s = this.selection;
55103         if(!s){
55104             e.stopEvent();
55105             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55106             if(cell){
55107                 this.select(cell[0], cell[1]);
55108             }
55109             return;
55110         }
55111         var sm = this;
55112         var walk = function(row, col, step){
55113             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55114         };
55115         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55116         var newCell;
55117
55118       
55119
55120         switch(k){
55121             case e.TAB:
55122                 // handled by onEditorKey
55123                 if (g.isEditor && g.editing) {
55124                     return;
55125                 }
55126                 if(e.shiftKey) {
55127                     newCell = walk(r, c-1, -1);
55128                 } else {
55129                     newCell = walk(r, c+1, 1);
55130                 }
55131                 break;
55132             
55133             case e.DOWN:
55134                newCell = walk(r+1, c, 1);
55135                 break;
55136             
55137             case e.UP:
55138                 newCell = walk(r-1, c, -1);
55139                 break;
55140             
55141             case e.RIGHT:
55142                 newCell = walk(r, c+1, 1);
55143                 break;
55144             
55145             case e.LEFT:
55146                 newCell = walk(r, c-1, -1);
55147                 break;
55148             
55149             case e.ENTER:
55150                 
55151                 if(g.isEditor && !g.editing){
55152                    g.startEditing(r, c);
55153                    e.stopEvent();
55154                    return;
55155                 }
55156                 
55157                 
55158              break;
55159         };
55160         if(newCell){
55161             this.select(newCell[0], newCell[1]);
55162             e.stopEvent();
55163             
55164         }
55165     },
55166
55167     acceptsNav : function(row, col, cm){
55168         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55169     },
55170     /**
55171      * Selects a cell.
55172      * @param {Number} field (not used) - as it's normally used as a listener
55173      * @param {Number} e - event - fake it by using
55174      *
55175      * var e = Roo.EventObjectImpl.prototype;
55176      * e.keyCode = e.TAB
55177      *
55178      * 
55179      */
55180     onEditorKey : function(field, e){
55181         
55182         var k = e.getKey(),
55183             newCell,
55184             g = this.grid,
55185             ed = g.activeEditor,
55186             forward = false;
55187         ///Roo.log('onEditorKey' + k);
55188         
55189         
55190         if (this.enter_is_tab && k == e.ENTER) {
55191             k = e.TAB;
55192         }
55193         
55194         if(k == e.TAB){
55195             if(e.shiftKey){
55196                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55197             }else{
55198                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55199                 forward = true;
55200             }
55201             
55202             e.stopEvent();
55203             
55204         } else if(k == e.ENTER &&  !e.ctrlKey){
55205             ed.completeEdit();
55206             e.stopEvent();
55207             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55208         
55209                 } else if(k == e.ESC){
55210             ed.cancelEdit();
55211         }
55212                 
55213         if (newCell) {
55214             var ecall = { cell : newCell, forward : forward };
55215             this.fireEvent('beforeeditnext', ecall );
55216             newCell = ecall.cell;
55217                         forward = ecall.forward;
55218         }
55219                 
55220         if(newCell){
55221             //Roo.log('next cell after edit');
55222             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55223         } else if (forward) {
55224             // tabbed past last
55225             this.fireEvent.defer(100, this, ['tabend',this]);
55226         }
55227     }
55228 });/*
55229  * Based on:
55230  * Ext JS Library 1.1.1
55231  * Copyright(c) 2006-2007, Ext JS, LLC.
55232  *
55233  * Originally Released Under LGPL - original licence link has changed is not relivant.
55234  *
55235  * Fork - LGPL
55236  * <script type="text/javascript">
55237  */
55238  
55239 /**
55240  * @class Roo.grid.EditorGrid
55241  * @extends Roo.grid.Grid
55242  * Class for creating and editable grid.
55243  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55244  * The container MUST have some type of size defined for the grid to fill. The container will be 
55245  * automatically set to position relative if it isn't already.
55246  * @param {Object} dataSource The data model to bind to
55247  * @param {Object} colModel The column model with info about this grid's columns
55248  */
55249 Roo.grid.EditorGrid = function(container, config){
55250     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55251     this.getGridEl().addClass("xedit-grid");
55252
55253     if(!this.selModel){
55254         this.selModel = new Roo.grid.CellSelectionModel();
55255     }
55256
55257     this.activeEditor = null;
55258
55259         this.addEvents({
55260             /**
55261              * @event beforeedit
55262              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55263              * <ul style="padding:5px;padding-left:16px;">
55264              * <li>grid - This grid</li>
55265              * <li>record - The record being edited</li>
55266              * <li>field - The field name being edited</li>
55267              * <li>value - The value for the field being edited.</li>
55268              * <li>row - The grid row index</li>
55269              * <li>column - The grid column index</li>
55270              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55271              * </ul>
55272              * @param {Object} e An edit event (see above for description)
55273              */
55274             "beforeedit" : true,
55275             /**
55276              * @event afteredit
55277              * Fires after a cell is edited. <br />
55278              * <ul style="padding:5px;padding-left:16px;">
55279              * <li>grid - This grid</li>
55280              * <li>record - The record being edited</li>
55281              * <li>field - The field name being edited</li>
55282              * <li>value - The value being set</li>
55283              * <li>originalValue - The original value for the field, before the edit.</li>
55284              * <li>row - The grid row index</li>
55285              * <li>column - The grid column index</li>
55286              * </ul>
55287              * @param {Object} e An edit event (see above for description)
55288              */
55289             "afteredit" : true,
55290             /**
55291              * @event validateedit
55292              * Fires after a cell is edited, but before the value is set in the record. 
55293          * You can use this to modify the value being set in the field, Return false
55294              * to cancel the change. The edit event object has the following properties <br />
55295              * <ul style="padding:5px;padding-left:16px;">
55296          * <li>editor - This editor</li>
55297              * <li>grid - This grid</li>
55298              * <li>record - The record being edited</li>
55299              * <li>field - The field name being edited</li>
55300              * <li>value - The value being set</li>
55301              * <li>originalValue - The original value for the field, before the edit.</li>
55302              * <li>row - The grid row index</li>
55303              * <li>column - The grid column index</li>
55304              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55305              * </ul>
55306              * @param {Object} e An edit event (see above for description)
55307              */
55308             "validateedit" : true
55309         });
55310     this.on("bodyscroll", this.stopEditing,  this);
55311     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55312 };
55313
55314 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55315     /**
55316      * @cfg {Number} clicksToEdit
55317      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55318      */
55319     clicksToEdit: 2,
55320
55321     // private
55322     isEditor : true,
55323     // private
55324     trackMouseOver: false, // causes very odd FF errors
55325
55326     onCellDblClick : function(g, row, col){
55327         this.startEditing(row, col);
55328     },
55329
55330     onEditComplete : function(ed, value, startValue){
55331         this.editing = false;
55332         this.activeEditor = null;
55333         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55334         var r = ed.record;
55335         var field = this.colModel.getDataIndex(ed.col);
55336         var e = {
55337             grid: this,
55338             record: r,
55339             field: field,
55340             originalValue: startValue,
55341             value: value,
55342             row: ed.row,
55343             column: ed.col,
55344             cancel:false,
55345             editor: ed
55346         };
55347         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55348         cell.show();
55349           
55350         if(String(value) !== String(startValue)){
55351             
55352             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55353                 r.set(field, e.value);
55354                 // if we are dealing with a combo box..
55355                 // then we also set the 'name' colum to be the displayField
55356                 if (ed.field.displayField && ed.field.name) {
55357                     r.set(ed.field.name, ed.field.el.dom.value);
55358                 }
55359                 
55360                 delete e.cancel; //?? why!!!
55361                 this.fireEvent("afteredit", e);
55362             }
55363         } else {
55364             this.fireEvent("afteredit", e); // always fire it!
55365         }
55366         this.view.focusCell(ed.row, ed.col);
55367     },
55368
55369     /**
55370      * Starts editing the specified for the specified row/column
55371      * @param {Number} rowIndex
55372      * @param {Number} colIndex
55373      */
55374     startEditing : function(row, col){
55375         this.stopEditing();
55376         if(this.colModel.isCellEditable(col, row)){
55377             this.view.ensureVisible(row, col, true);
55378           
55379             var r = this.dataSource.getAt(row);
55380             var field = this.colModel.getDataIndex(col);
55381             var cell = Roo.get(this.view.getCell(row,col));
55382             var e = {
55383                 grid: this,
55384                 record: r,
55385                 field: field,
55386                 value: r.data[field],
55387                 row: row,
55388                 column: col,
55389                 cancel:false 
55390             };
55391             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55392                 this.editing = true;
55393                 var ed = this.colModel.getCellEditor(col, row);
55394                 
55395                 if (!ed) {
55396                     return;
55397                 }
55398                 if(!ed.rendered){
55399                     ed.render(ed.parentEl || document.body);
55400                 }
55401                 ed.field.reset();
55402                
55403                 cell.hide();
55404                 
55405                 (function(){ // complex but required for focus issues in safari, ie and opera
55406                     ed.row = row;
55407                     ed.col = col;
55408                     ed.record = r;
55409                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55410                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55411                     this.activeEditor = ed;
55412                     var v = r.data[field];
55413                     ed.startEdit(this.view.getCell(row, col), v);
55414                     // combo's with 'displayField and name set
55415                     if (ed.field.displayField && ed.field.name) {
55416                         ed.field.el.dom.value = r.data[ed.field.name];
55417                     }
55418                     
55419                     
55420                 }).defer(50, this);
55421             }
55422         }
55423     },
55424         
55425     /**
55426      * Stops any active editing
55427      */
55428     stopEditing : function(){
55429         if(this.activeEditor){
55430             this.activeEditor.completeEdit();
55431         }
55432         this.activeEditor = null;
55433     },
55434         
55435          /**
55436      * Called to get grid's drag proxy text, by default returns this.ddText.
55437      * @return {String}
55438      */
55439     getDragDropText : function(){
55440         var count = this.selModel.getSelectedCell() ? 1 : 0;
55441         return String.format(this.ddText, count, count == 1 ? '' : 's');
55442     }
55443         
55444 });/*
55445  * Based on:
55446  * Ext JS Library 1.1.1
55447  * Copyright(c) 2006-2007, Ext JS, LLC.
55448  *
55449  * Originally Released Under LGPL - original licence link has changed is not relivant.
55450  *
55451  * Fork - LGPL
55452  * <script type="text/javascript">
55453  */
55454
55455 // private - not really -- you end up using it !
55456 // This is a support class used internally by the Grid components
55457
55458 /**
55459  * @class Roo.grid.GridEditor
55460  * @extends Roo.Editor
55461  * Class for creating and editable grid elements.
55462  * @param {Object} config any settings (must include field)
55463  */
55464 Roo.grid.GridEditor = function(field, config){
55465     if (!config && field.field) {
55466         config = field;
55467         field = Roo.factory(config.field, Roo.form);
55468     }
55469     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55470     field.monitorTab = false;
55471 };
55472
55473 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55474     
55475     /**
55476      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55477      */
55478     
55479     alignment: "tl-tl",
55480     autoSize: "width",
55481     hideEl : false,
55482     cls: "x-small-editor x-grid-editor",
55483     shim:false,
55484     shadow:"frame"
55485 });/*
55486  * Based on:
55487  * Ext JS Library 1.1.1
55488  * Copyright(c) 2006-2007, Ext JS, LLC.
55489  *
55490  * Originally Released Under LGPL - original licence link has changed is not relivant.
55491  *
55492  * Fork - LGPL
55493  * <script type="text/javascript">
55494  */
55495   
55496
55497   
55498 Roo.grid.PropertyRecord = Roo.data.Record.create([
55499     {name:'name',type:'string'},  'value'
55500 ]);
55501
55502
55503 Roo.grid.PropertyStore = function(grid, source){
55504     this.grid = grid;
55505     this.store = new Roo.data.Store({
55506         recordType : Roo.grid.PropertyRecord
55507     });
55508     this.store.on('update', this.onUpdate,  this);
55509     if(source){
55510         this.setSource(source);
55511     }
55512     Roo.grid.PropertyStore.superclass.constructor.call(this);
55513 };
55514
55515
55516
55517 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55518     setSource : function(o){
55519         this.source = o;
55520         this.store.removeAll();
55521         var data = [];
55522         for(var k in o){
55523             if(this.isEditableValue(o[k])){
55524                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55525             }
55526         }
55527         this.store.loadRecords({records: data}, {}, true);
55528     },
55529
55530     onUpdate : function(ds, record, type){
55531         if(type == Roo.data.Record.EDIT){
55532             var v = record.data['value'];
55533             var oldValue = record.modified['value'];
55534             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55535                 this.source[record.id] = v;
55536                 record.commit();
55537                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55538             }else{
55539                 record.reject();
55540             }
55541         }
55542     },
55543
55544     getProperty : function(row){
55545        return this.store.getAt(row);
55546     },
55547
55548     isEditableValue: function(val){
55549         if(val && val instanceof Date){
55550             return true;
55551         }else if(typeof val == 'object' || typeof val == 'function'){
55552             return false;
55553         }
55554         return true;
55555     },
55556
55557     setValue : function(prop, value){
55558         this.source[prop] = value;
55559         this.store.getById(prop).set('value', value);
55560     },
55561
55562     getSource : function(){
55563         return this.source;
55564     }
55565 });
55566
55567 Roo.grid.PropertyColumnModel = function(grid, store){
55568     this.grid = grid;
55569     var g = Roo.grid;
55570     g.PropertyColumnModel.superclass.constructor.call(this, [
55571         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55572         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55573     ]);
55574     this.store = store;
55575     this.bselect = Roo.DomHelper.append(document.body, {
55576         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55577             {tag: 'option', value: 'true', html: 'true'},
55578             {tag: 'option', value: 'false', html: 'false'}
55579         ]
55580     });
55581     Roo.id(this.bselect);
55582     var f = Roo.form;
55583     this.editors = {
55584         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55585         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55586         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55587         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55588         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55589     };
55590     this.renderCellDelegate = this.renderCell.createDelegate(this);
55591     this.renderPropDelegate = this.renderProp.createDelegate(this);
55592 };
55593
55594 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55595     
55596     
55597     nameText : 'Name',
55598     valueText : 'Value',
55599     
55600     dateFormat : 'm/j/Y',
55601     
55602     
55603     renderDate : function(dateVal){
55604         return dateVal.dateFormat(this.dateFormat);
55605     },
55606
55607     renderBool : function(bVal){
55608         return bVal ? 'true' : 'false';
55609     },
55610
55611     isCellEditable : function(colIndex, rowIndex){
55612         return colIndex == 1;
55613     },
55614
55615     getRenderer : function(col){
55616         return col == 1 ?
55617             this.renderCellDelegate : this.renderPropDelegate;
55618     },
55619
55620     renderProp : function(v){
55621         return this.getPropertyName(v);
55622     },
55623
55624     renderCell : function(val){
55625         var rv = val;
55626         if(val instanceof Date){
55627             rv = this.renderDate(val);
55628         }else if(typeof val == 'boolean'){
55629             rv = this.renderBool(val);
55630         }
55631         return Roo.util.Format.htmlEncode(rv);
55632     },
55633
55634     getPropertyName : function(name){
55635         var pn = this.grid.propertyNames;
55636         return pn && pn[name] ? pn[name] : name;
55637     },
55638
55639     getCellEditor : function(colIndex, rowIndex){
55640         var p = this.store.getProperty(rowIndex);
55641         var n = p.data['name'], val = p.data['value'];
55642         
55643         if(typeof(this.grid.customEditors[n]) == 'string'){
55644             return this.editors[this.grid.customEditors[n]];
55645         }
55646         if(typeof(this.grid.customEditors[n]) != 'undefined'){
55647             return this.grid.customEditors[n];
55648         }
55649         if(val instanceof Date){
55650             return this.editors['date'];
55651         }else if(typeof val == 'number'){
55652             return this.editors['number'];
55653         }else if(typeof val == 'boolean'){
55654             return this.editors['boolean'];
55655         }else{
55656             return this.editors['string'];
55657         }
55658     }
55659 });
55660
55661 /**
55662  * @class Roo.grid.PropertyGrid
55663  * @extends Roo.grid.EditorGrid
55664  * This class represents the  interface of a component based property grid control.
55665  * <br><br>Usage:<pre><code>
55666  var grid = new Roo.grid.PropertyGrid("my-container-id", {
55667       
55668  });
55669  // set any options
55670  grid.render();
55671  * </code></pre>
55672   
55673  * @constructor
55674  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55675  * The container MUST have some type of size defined for the grid to fill. The container will be
55676  * automatically set to position relative if it isn't already.
55677  * @param {Object} config A config object that sets properties on this grid.
55678  */
55679 Roo.grid.PropertyGrid = function(container, config){
55680     config = config || {};
55681     var store = new Roo.grid.PropertyStore(this);
55682     this.store = store;
55683     var cm = new Roo.grid.PropertyColumnModel(this, store);
55684     store.store.sort('name', 'ASC');
55685     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
55686         ds: store.store,
55687         cm: cm,
55688         enableColLock:false,
55689         enableColumnMove:false,
55690         stripeRows:false,
55691         trackMouseOver: false,
55692         clicksToEdit:1
55693     }, config));
55694     this.getGridEl().addClass('x-props-grid');
55695     this.lastEditRow = null;
55696     this.on('columnresize', this.onColumnResize, this);
55697     this.addEvents({
55698          /**
55699              * @event beforepropertychange
55700              * Fires before a property changes (return false to stop?)
55701              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55702              * @param {String} id Record Id
55703              * @param {String} newval New Value
55704          * @param {String} oldval Old Value
55705              */
55706         "beforepropertychange": true,
55707         /**
55708              * @event propertychange
55709              * Fires after a property changes
55710              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
55711              * @param {String} id Record Id
55712              * @param {String} newval New Value
55713          * @param {String} oldval Old Value
55714              */
55715         "propertychange": true
55716     });
55717     this.customEditors = this.customEditors || {};
55718 };
55719 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
55720     
55721      /**
55722      * @cfg {Object} customEditors map of colnames=> custom editors.
55723      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
55724      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
55725      * false disables editing of the field.
55726          */
55727     
55728       /**
55729      * @cfg {Object} propertyNames map of property Names to their displayed value
55730          */
55731     
55732     render : function(){
55733         Roo.grid.PropertyGrid.superclass.render.call(this);
55734         this.autoSize.defer(100, this);
55735     },
55736
55737     autoSize : function(){
55738         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
55739         if(this.view){
55740             this.view.fitColumns();
55741         }
55742     },
55743
55744     onColumnResize : function(){
55745         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
55746         this.autoSize();
55747     },
55748     /**
55749      * Sets the data for the Grid
55750      * accepts a Key => Value object of all the elements avaiable.
55751      * @param {Object} data  to appear in grid.
55752      */
55753     setSource : function(source){
55754         this.store.setSource(source);
55755         //this.autoSize();
55756     },
55757     /**
55758      * Gets all the data from the grid.
55759      * @return {Object} data  data stored in grid
55760      */
55761     getSource : function(){
55762         return this.store.getSource();
55763     }
55764 });/*
55765   
55766  * Licence LGPL
55767  
55768  */
55769  
55770 /**
55771  * @class Roo.grid.Calendar
55772  * @extends Roo.util.Grid
55773  * This class extends the Grid to provide a calendar widget
55774  * <br><br>Usage:<pre><code>
55775  var grid = new Roo.grid.Calendar("my-container-id", {
55776      ds: myDataStore,
55777      cm: myColModel,
55778      selModel: mySelectionModel,
55779      autoSizeColumns: true,
55780      monitorWindowResize: false,
55781      trackMouseOver: true
55782      eventstore : real data store..
55783  });
55784  // set any options
55785  grid.render();
55786   
55787   * @constructor
55788  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55789  * The container MUST have some type of size defined for the grid to fill. The container will be
55790  * automatically set to position relative if it isn't already.
55791  * @param {Object} config A config object that sets properties on this grid.
55792  */
55793 Roo.grid.Calendar = function(container, config){
55794         // initialize the container
55795         this.container = Roo.get(container);
55796         this.container.update("");
55797         this.container.setStyle("overflow", "hidden");
55798     this.container.addClass('x-grid-container');
55799
55800     this.id = this.container.id;
55801
55802     Roo.apply(this, config);
55803     // check and correct shorthanded configs
55804     
55805     var rows = [];
55806     var d =1;
55807     for (var r = 0;r < 6;r++) {
55808         
55809         rows[r]=[];
55810         for (var c =0;c < 7;c++) {
55811             rows[r][c]= '';
55812         }
55813     }
55814     if (this.eventStore) {
55815         this.eventStore= Roo.factory(this.eventStore, Roo.data);
55816         this.eventStore.on('load',this.onLoad, this);
55817         this.eventStore.on('beforeload',this.clearEvents, this);
55818          
55819     }
55820     
55821     this.dataSource = new Roo.data.Store({
55822             proxy: new Roo.data.MemoryProxy(rows),
55823             reader: new Roo.data.ArrayReader({}, [
55824                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
55825     });
55826
55827     this.dataSource.load();
55828     this.ds = this.dataSource;
55829     this.ds.xmodule = this.xmodule || false;
55830     
55831     
55832     var cellRender = function(v,x,r)
55833     {
55834         return String.format(
55835             '<div class="fc-day  fc-widget-content"><div>' +
55836                 '<div class="fc-event-container"></div>' +
55837                 '<div class="fc-day-number">{0}</div>'+
55838                 
55839                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
55840             '</div></div>', v);
55841     
55842     }
55843     
55844     
55845     this.colModel = new Roo.grid.ColumnModel( [
55846         {
55847             xtype: 'ColumnModel',
55848             xns: Roo.grid,
55849             dataIndex : 'weekday0',
55850             header : 'Sunday',
55851             renderer : cellRender
55852         },
55853         {
55854             xtype: 'ColumnModel',
55855             xns: Roo.grid,
55856             dataIndex : 'weekday1',
55857             header : 'Monday',
55858             renderer : cellRender
55859         },
55860         {
55861             xtype: 'ColumnModel',
55862             xns: Roo.grid,
55863             dataIndex : 'weekday2',
55864             header : 'Tuesday',
55865             renderer : cellRender
55866         },
55867         {
55868             xtype: 'ColumnModel',
55869             xns: Roo.grid,
55870             dataIndex : 'weekday3',
55871             header : 'Wednesday',
55872             renderer : cellRender
55873         },
55874         {
55875             xtype: 'ColumnModel',
55876             xns: Roo.grid,
55877             dataIndex : 'weekday4',
55878             header : 'Thursday',
55879             renderer : cellRender
55880         },
55881         {
55882             xtype: 'ColumnModel',
55883             xns: Roo.grid,
55884             dataIndex : 'weekday5',
55885             header : 'Friday',
55886             renderer : cellRender
55887         },
55888         {
55889             xtype: 'ColumnModel',
55890             xns: Roo.grid,
55891             dataIndex : 'weekday6',
55892             header : 'Saturday',
55893             renderer : cellRender
55894         }
55895     ]);
55896     this.cm = this.colModel;
55897     this.cm.xmodule = this.xmodule || false;
55898  
55899         
55900           
55901     //this.selModel = new Roo.grid.CellSelectionModel();
55902     //this.sm = this.selModel;
55903     //this.selModel.init(this);
55904     
55905     
55906     if(this.width){
55907         this.container.setWidth(this.width);
55908     }
55909
55910     if(this.height){
55911         this.container.setHeight(this.height);
55912     }
55913     /** @private */
55914         this.addEvents({
55915         // raw events
55916         /**
55917          * @event click
55918          * The raw click event for the entire grid.
55919          * @param {Roo.EventObject} e
55920          */
55921         "click" : true,
55922         /**
55923          * @event dblclick
55924          * The raw dblclick event for the entire grid.
55925          * @param {Roo.EventObject} e
55926          */
55927         "dblclick" : true,
55928         /**
55929          * @event contextmenu
55930          * The raw contextmenu event for the entire grid.
55931          * @param {Roo.EventObject} e
55932          */
55933         "contextmenu" : true,
55934         /**
55935          * @event mousedown
55936          * The raw mousedown event for the entire grid.
55937          * @param {Roo.EventObject} e
55938          */
55939         "mousedown" : true,
55940         /**
55941          * @event mouseup
55942          * The raw mouseup event for the entire grid.
55943          * @param {Roo.EventObject} e
55944          */
55945         "mouseup" : true,
55946         /**
55947          * @event mouseover
55948          * The raw mouseover event for the entire grid.
55949          * @param {Roo.EventObject} e
55950          */
55951         "mouseover" : true,
55952         /**
55953          * @event mouseout
55954          * The raw mouseout event for the entire grid.
55955          * @param {Roo.EventObject} e
55956          */
55957         "mouseout" : true,
55958         /**
55959          * @event keypress
55960          * The raw keypress event for the entire grid.
55961          * @param {Roo.EventObject} e
55962          */
55963         "keypress" : true,
55964         /**
55965          * @event keydown
55966          * The raw keydown event for the entire grid.
55967          * @param {Roo.EventObject} e
55968          */
55969         "keydown" : true,
55970
55971         // custom events
55972
55973         /**
55974          * @event cellclick
55975          * Fires when a cell is clicked
55976          * @param {Grid} this
55977          * @param {Number} rowIndex
55978          * @param {Number} columnIndex
55979          * @param {Roo.EventObject} e
55980          */
55981         "cellclick" : true,
55982         /**
55983          * @event celldblclick
55984          * Fires when a cell is double clicked
55985          * @param {Grid} this
55986          * @param {Number} rowIndex
55987          * @param {Number} columnIndex
55988          * @param {Roo.EventObject} e
55989          */
55990         "celldblclick" : true,
55991         /**
55992          * @event rowclick
55993          * Fires when a row is clicked
55994          * @param {Grid} this
55995          * @param {Number} rowIndex
55996          * @param {Roo.EventObject} e
55997          */
55998         "rowclick" : true,
55999         /**
56000          * @event rowdblclick
56001          * Fires when a row is double clicked
56002          * @param {Grid} this
56003          * @param {Number} rowIndex
56004          * @param {Roo.EventObject} e
56005          */
56006         "rowdblclick" : true,
56007         /**
56008          * @event headerclick
56009          * Fires when a header is clicked
56010          * @param {Grid} this
56011          * @param {Number} columnIndex
56012          * @param {Roo.EventObject} e
56013          */
56014         "headerclick" : true,
56015         /**
56016          * @event headerdblclick
56017          * Fires when a header cell is double clicked
56018          * @param {Grid} this
56019          * @param {Number} columnIndex
56020          * @param {Roo.EventObject} e
56021          */
56022         "headerdblclick" : true,
56023         /**
56024          * @event rowcontextmenu
56025          * Fires when a row is right clicked
56026          * @param {Grid} this
56027          * @param {Number} rowIndex
56028          * @param {Roo.EventObject} e
56029          */
56030         "rowcontextmenu" : true,
56031         /**
56032          * @event cellcontextmenu
56033          * Fires when a cell is right clicked
56034          * @param {Grid} this
56035          * @param {Number} rowIndex
56036          * @param {Number} cellIndex
56037          * @param {Roo.EventObject} e
56038          */
56039          "cellcontextmenu" : true,
56040         /**
56041          * @event headercontextmenu
56042          * Fires when a header is right clicked
56043          * @param {Grid} this
56044          * @param {Number} columnIndex
56045          * @param {Roo.EventObject} e
56046          */
56047         "headercontextmenu" : true,
56048         /**
56049          * @event bodyscroll
56050          * Fires when the body element is scrolled
56051          * @param {Number} scrollLeft
56052          * @param {Number} scrollTop
56053          */
56054         "bodyscroll" : true,
56055         /**
56056          * @event columnresize
56057          * Fires when the user resizes a column
56058          * @param {Number} columnIndex
56059          * @param {Number} newSize
56060          */
56061         "columnresize" : true,
56062         /**
56063          * @event columnmove
56064          * Fires when the user moves a column
56065          * @param {Number} oldIndex
56066          * @param {Number} newIndex
56067          */
56068         "columnmove" : true,
56069         /**
56070          * @event startdrag
56071          * Fires when row(s) start being dragged
56072          * @param {Grid} this
56073          * @param {Roo.GridDD} dd The drag drop object
56074          * @param {event} e The raw browser event
56075          */
56076         "startdrag" : true,
56077         /**
56078          * @event enddrag
56079          * Fires when a drag operation is complete
56080          * @param {Grid} this
56081          * @param {Roo.GridDD} dd The drag drop object
56082          * @param {event} e The raw browser event
56083          */
56084         "enddrag" : true,
56085         /**
56086          * @event dragdrop
56087          * Fires when dragged row(s) are dropped on a valid DD target
56088          * @param {Grid} this
56089          * @param {Roo.GridDD} dd The drag drop object
56090          * @param {String} targetId The target drag drop object
56091          * @param {event} e The raw browser event
56092          */
56093         "dragdrop" : true,
56094         /**
56095          * @event dragover
56096          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56097          * @param {Grid} this
56098          * @param {Roo.GridDD} dd The drag drop object
56099          * @param {String} targetId The target drag drop object
56100          * @param {event} e The raw browser event
56101          */
56102         "dragover" : true,
56103         /**
56104          * @event dragenter
56105          *  Fires when the dragged row(s) first cross another DD target while being dragged
56106          * @param {Grid} this
56107          * @param {Roo.GridDD} dd The drag drop object
56108          * @param {String} targetId The target drag drop object
56109          * @param {event} e The raw browser event
56110          */
56111         "dragenter" : true,
56112         /**
56113          * @event dragout
56114          * Fires when the dragged row(s) leave another DD target while being dragged
56115          * @param {Grid} this
56116          * @param {Roo.GridDD} dd The drag drop object
56117          * @param {String} targetId The target drag drop object
56118          * @param {event} e The raw browser event
56119          */
56120         "dragout" : true,
56121         /**
56122          * @event rowclass
56123          * Fires when a row is rendered, so you can change add a style to it.
56124          * @param {GridView} gridview   The grid view
56125          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56126          */
56127         'rowclass' : true,
56128
56129         /**
56130          * @event render
56131          * Fires when the grid is rendered
56132          * @param {Grid} grid
56133          */
56134         'render' : true,
56135             /**
56136              * @event select
56137              * Fires when a date is selected
56138              * @param {DatePicker} this
56139              * @param {Date} date The selected date
56140              */
56141         'select': true,
56142         /**
56143              * @event monthchange
56144              * Fires when the displayed month changes 
56145              * @param {DatePicker} this
56146              * @param {Date} date The selected month
56147              */
56148         'monthchange': true,
56149         /**
56150              * @event evententer
56151              * Fires when mouse over an event
56152              * @param {Calendar} this
56153              * @param {event} Event
56154              */
56155         'evententer': true,
56156         /**
56157              * @event eventleave
56158              * Fires when the mouse leaves an
56159              * @param {Calendar} this
56160              * @param {event}
56161              */
56162         'eventleave': true,
56163         /**
56164              * @event eventclick
56165              * Fires when the mouse click an
56166              * @param {Calendar} this
56167              * @param {event}
56168              */
56169         'eventclick': true,
56170         /**
56171              * @event eventrender
56172              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56173              * @param {Calendar} this
56174              * @param {data} data to be modified
56175              */
56176         'eventrender': true
56177         
56178     });
56179
56180     Roo.grid.Grid.superclass.constructor.call(this);
56181     this.on('render', function() {
56182         this.view.el.addClass('x-grid-cal'); 
56183         
56184         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56185
56186     },this);
56187     
56188     if (!Roo.grid.Calendar.style) {
56189         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56190             
56191             
56192             '.x-grid-cal .x-grid-col' :  {
56193                 height: 'auto !important',
56194                 'vertical-align': 'top'
56195             },
56196             '.x-grid-cal  .fc-event-hori' : {
56197                 height: '14px'
56198             }
56199              
56200             
56201         }, Roo.id());
56202     }
56203
56204     
56205     
56206 };
56207 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56208     /**
56209      * @cfg {Store} eventStore The store that loads events.
56210      */
56211     eventStore : 25,
56212
56213      
56214     activeDate : false,
56215     startDay : 0,
56216     autoWidth : true,
56217     monitorWindowResize : false,
56218
56219     
56220     resizeColumns : function() {
56221         var col = (this.view.el.getWidth() / 7) - 3;
56222         // loop through cols, and setWidth
56223         for(var i =0 ; i < 7 ; i++){
56224             this.cm.setColumnWidth(i, col);
56225         }
56226     },
56227      setDate :function(date) {
56228         
56229         Roo.log('setDate?');
56230         
56231         this.resizeColumns();
56232         var vd = this.activeDate;
56233         this.activeDate = date;
56234 //        if(vd && this.el){
56235 //            var t = date.getTime();
56236 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56237 //                Roo.log('using add remove');
56238 //                
56239 //                this.fireEvent('monthchange', this, date);
56240 //                
56241 //                this.cells.removeClass("fc-state-highlight");
56242 //                this.cells.each(function(c){
56243 //                   if(c.dateValue == t){
56244 //                       c.addClass("fc-state-highlight");
56245 //                       setTimeout(function(){
56246 //                            try{c.dom.firstChild.focus();}catch(e){}
56247 //                       }, 50);
56248 //                       return false;
56249 //                   }
56250 //                   return true;
56251 //                });
56252 //                return;
56253 //            }
56254 //        }
56255         
56256         var days = date.getDaysInMonth();
56257         
56258         var firstOfMonth = date.getFirstDateOfMonth();
56259         var startingPos = firstOfMonth.getDay()-this.startDay;
56260         
56261         if(startingPos < this.startDay){
56262             startingPos += 7;
56263         }
56264         
56265         var pm = date.add(Date.MONTH, -1);
56266         var prevStart = pm.getDaysInMonth()-startingPos;
56267 //        
56268         
56269         
56270         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56271         
56272         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
56273         //this.cells.addClassOnOver('fc-state-hover');
56274         
56275         var cells = this.cells.elements;
56276         var textEls = this.textNodes;
56277         
56278         //Roo.each(cells, function(cell){
56279         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
56280         //});
56281         
56282         days += startingPos;
56283
56284         // convert everything to numbers so it's fast
56285         var day = 86400000;
56286         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
56287         //Roo.log(d);
56288         //Roo.log(pm);
56289         //Roo.log(prevStart);
56290         
56291         var today = new Date().clearTime().getTime();
56292         var sel = date.clearTime().getTime();
56293         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
56294         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
56295         var ddMatch = this.disabledDatesRE;
56296         var ddText = this.disabledDatesText;
56297         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
56298         var ddaysText = this.disabledDaysText;
56299         var format = this.format;
56300         
56301         var setCellClass = function(cal, cell){
56302             
56303             //Roo.log('set Cell Class');
56304             cell.title = "";
56305             var t = d.getTime();
56306             
56307             //Roo.log(d);
56308             
56309             
56310             cell.dateValue = t;
56311             if(t == today){
56312                 cell.className += " fc-today";
56313                 cell.className += " fc-state-highlight";
56314                 cell.title = cal.todayText;
56315             }
56316             if(t == sel){
56317                 // disable highlight in other month..
56318                 cell.className += " fc-state-highlight";
56319                 
56320             }
56321             // disabling
56322             if(t < min) {
56323                 //cell.className = " fc-state-disabled";
56324                 cell.title = cal.minText;
56325                 return;
56326             }
56327             if(t > max) {
56328                 //cell.className = " fc-state-disabled";
56329                 cell.title = cal.maxText;
56330                 return;
56331             }
56332             if(ddays){
56333                 if(ddays.indexOf(d.getDay()) != -1){
56334                     // cell.title = ddaysText;
56335                    // cell.className = " fc-state-disabled";
56336                 }
56337             }
56338             if(ddMatch && format){
56339                 var fvalue = d.dateFormat(format);
56340                 if(ddMatch.test(fvalue)){
56341                     cell.title = ddText.replace("%0", fvalue);
56342                    cell.className = " fc-state-disabled";
56343                 }
56344             }
56345             
56346             if (!cell.initialClassName) {
56347                 cell.initialClassName = cell.dom.className;
56348             }
56349             
56350             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
56351         };
56352
56353         var i = 0;
56354         
56355         for(; i < startingPos; i++) {
56356             cells[i].dayName =  (++prevStart);
56357             Roo.log(textEls[i]);
56358             d.setDate(d.getDate()+1);
56359             
56360             //cells[i].className = "fc-past fc-other-month";
56361             setCellClass(this, cells[i]);
56362         }
56363         
56364         var intDay = 0;
56365         
56366         for(; i < days; i++){
56367             intDay = i - startingPos + 1;
56368             cells[i].dayName =  (intDay);
56369             d.setDate(d.getDate()+1);
56370             
56371             cells[i].className = ''; // "x-date-active";
56372             setCellClass(this, cells[i]);
56373         }
56374         var extraDays = 0;
56375         
56376         for(; i < 42; i++) {
56377             //textEls[i].innerHTML = (++extraDays);
56378             
56379             d.setDate(d.getDate()+1);
56380             cells[i].dayName = (++extraDays);
56381             cells[i].className = "fc-future fc-other-month";
56382             setCellClass(this, cells[i]);
56383         }
56384         
56385         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
56386         
56387         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
56388         
56389         // this will cause all the cells to mis
56390         var rows= [];
56391         var i =0;
56392         for (var r = 0;r < 6;r++) {
56393             for (var c =0;c < 7;c++) {
56394                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
56395             }    
56396         }
56397         
56398         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56399         for(i=0;i<cells.length;i++) {
56400             
56401             this.cells.elements[i].dayName = cells[i].dayName ;
56402             this.cells.elements[i].className = cells[i].className;
56403             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
56404             this.cells.elements[i].title = cells[i].title ;
56405             this.cells.elements[i].dateValue = cells[i].dateValue ;
56406         }
56407         
56408         
56409         
56410         
56411         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
56412         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
56413         
56414         ////if(totalRows != 6){
56415             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
56416            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
56417        // }
56418         
56419         this.fireEvent('monthchange', this, date);
56420         
56421         
56422     },
56423  /**
56424      * Returns the grid's SelectionModel.
56425      * @return {SelectionModel}
56426      */
56427     getSelectionModel : function(){
56428         if(!this.selModel){
56429             this.selModel = new Roo.grid.CellSelectionModel();
56430         }
56431         return this.selModel;
56432     },
56433
56434     load: function() {
56435         this.eventStore.load()
56436         
56437         
56438         
56439     },
56440     
56441     findCell : function(dt) {
56442         dt = dt.clearTime().getTime();
56443         var ret = false;
56444         this.cells.each(function(c){
56445             //Roo.log("check " +c.dateValue + '?=' + dt);
56446             if(c.dateValue == dt){
56447                 ret = c;
56448                 return false;
56449             }
56450             return true;
56451         });
56452         
56453         return ret;
56454     },
56455     
56456     findCells : function(rec) {
56457         var s = rec.data.start_dt.clone().clearTime().getTime();
56458        // Roo.log(s);
56459         var e= rec.data.end_dt.clone().clearTime().getTime();
56460        // Roo.log(e);
56461         var ret = [];
56462         this.cells.each(function(c){
56463              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
56464             
56465             if(c.dateValue > e){
56466                 return ;
56467             }
56468             if(c.dateValue < s){
56469                 return ;
56470             }
56471             ret.push(c);
56472         });
56473         
56474         return ret;    
56475     },
56476     
56477     findBestRow: function(cells)
56478     {
56479         var ret = 0;
56480         
56481         for (var i =0 ; i < cells.length;i++) {
56482             ret  = Math.max(cells[i].rows || 0,ret);
56483         }
56484         return ret;
56485         
56486     },
56487     
56488     
56489     addItem : function(rec)
56490     {
56491         // look for vertical location slot in
56492         var cells = this.findCells(rec);
56493         
56494         rec.row = this.findBestRow(cells);
56495         
56496         // work out the location.
56497         
56498         var crow = false;
56499         var rows = [];
56500         for(var i =0; i < cells.length; i++) {
56501             if (!crow) {
56502                 crow = {
56503                     start : cells[i],
56504                     end :  cells[i]
56505                 };
56506                 continue;
56507             }
56508             if (crow.start.getY() == cells[i].getY()) {
56509                 // on same row.
56510                 crow.end = cells[i];
56511                 continue;
56512             }
56513             // different row.
56514             rows.push(crow);
56515             crow = {
56516                 start: cells[i],
56517                 end : cells[i]
56518             };
56519             
56520         }
56521         
56522         rows.push(crow);
56523         rec.els = [];
56524         rec.rows = rows;
56525         rec.cells = cells;
56526         for (var i = 0; i < cells.length;i++) {
56527             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
56528             
56529         }
56530         
56531         
56532     },
56533     
56534     clearEvents: function() {
56535         
56536         if (!this.eventStore.getCount()) {
56537             return;
56538         }
56539         // reset number of rows in cells.
56540         Roo.each(this.cells.elements, function(c){
56541             c.rows = 0;
56542         });
56543         
56544         this.eventStore.each(function(e) {
56545             this.clearEvent(e);
56546         },this);
56547         
56548     },
56549     
56550     clearEvent : function(ev)
56551     {
56552         if (ev.els) {
56553             Roo.each(ev.els, function(el) {
56554                 el.un('mouseenter' ,this.onEventEnter, this);
56555                 el.un('mouseleave' ,this.onEventLeave, this);
56556                 el.remove();
56557             },this);
56558             ev.els = [];
56559         }
56560     },
56561     
56562     
56563     renderEvent : function(ev,ctr) {
56564         if (!ctr) {
56565              ctr = this.view.el.select('.fc-event-container',true).first();
56566         }
56567         
56568          
56569         this.clearEvent(ev);
56570             //code
56571        
56572         
56573         
56574         ev.els = [];
56575         var cells = ev.cells;
56576         var rows = ev.rows;
56577         this.fireEvent('eventrender', this, ev);
56578         
56579         for(var i =0; i < rows.length; i++) {
56580             
56581             cls = '';
56582             if (i == 0) {
56583                 cls += ' fc-event-start';
56584             }
56585             if ((i+1) == rows.length) {
56586                 cls += ' fc-event-end';
56587             }
56588             
56589             //Roo.log(ev.data);
56590             // how many rows should it span..
56591             var cg = this.eventTmpl.append(ctr,Roo.apply({
56592                 fccls : cls
56593                 
56594             }, ev.data) , true);
56595             
56596             
56597             cg.on('mouseenter' ,this.onEventEnter, this, ev);
56598             cg.on('mouseleave' ,this.onEventLeave, this, ev);
56599             cg.on('click', this.onEventClick, this, ev);
56600             
56601             ev.els.push(cg);
56602             
56603             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
56604             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
56605             //Roo.log(cg);
56606              
56607             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
56608             cg.setWidth(ebox.right - sbox.x -2);
56609         }
56610     },
56611     
56612     renderEvents: function()
56613     {   
56614         // first make sure there is enough space..
56615         
56616         if (!this.eventTmpl) {
56617             this.eventTmpl = new Roo.Template(
56618                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
56619                     '<div class="fc-event-inner">' +
56620                         '<span class="fc-event-time">{time}</span>' +
56621                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
56622                     '</div>' +
56623                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
56624                 '</div>'
56625             );
56626                 
56627         }
56628                
56629         
56630         
56631         this.cells.each(function(c) {
56632             //Roo.log(c.select('.fc-day-content div',true).first());
56633             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
56634         });
56635         
56636         var ctr = this.view.el.select('.fc-event-container',true).first();
56637         
56638         var cls;
56639         this.eventStore.each(function(ev){
56640             
56641             this.renderEvent(ev);
56642              
56643              
56644         }, this);
56645         this.view.layout();
56646         
56647     },
56648     
56649     onEventEnter: function (e, el,event,d) {
56650         this.fireEvent('evententer', this, el, event);
56651     },
56652     
56653     onEventLeave: function (e, el,event,d) {
56654         this.fireEvent('eventleave', this, el, event);
56655     },
56656     
56657     onEventClick: function (e, el,event,d) {
56658         this.fireEvent('eventclick', this, el, event);
56659     },
56660     
56661     onMonthChange: function () {
56662         this.store.load();
56663     },
56664     
56665     onLoad: function () {
56666         
56667         //Roo.log('calendar onload');
56668 //         
56669         if(this.eventStore.getCount() > 0){
56670             
56671            
56672             
56673             this.eventStore.each(function(d){
56674                 
56675                 
56676                 // FIXME..
56677                 var add =   d.data;
56678                 if (typeof(add.end_dt) == 'undefined')  {
56679                     Roo.log("Missing End time in calendar data: ");
56680                     Roo.log(d);
56681                     return;
56682                 }
56683                 if (typeof(add.start_dt) == 'undefined')  {
56684                     Roo.log("Missing Start time in calendar data: ");
56685                     Roo.log(d);
56686                     return;
56687                 }
56688                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
56689                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
56690                 add.id = add.id || d.id;
56691                 add.title = add.title || '??';
56692                 
56693                 this.addItem(d);
56694                 
56695              
56696             },this);
56697         }
56698         
56699         this.renderEvents();
56700     }
56701     
56702
56703 });
56704 /*
56705  grid : {
56706                 xtype: 'Grid',
56707                 xns: Roo.grid,
56708                 listeners : {
56709                     render : function ()
56710                     {
56711                         _this.grid = this;
56712                         
56713                         if (!this.view.el.hasClass('course-timesheet')) {
56714                             this.view.el.addClass('course-timesheet');
56715                         }
56716                         if (this.tsStyle) {
56717                             this.ds.load({});
56718                             return; 
56719                         }
56720                         Roo.log('width');
56721                         Roo.log(_this.grid.view.el.getWidth());
56722                         
56723                         
56724                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
56725                             '.course-timesheet .x-grid-row' : {
56726                                 height: '80px'
56727                             },
56728                             '.x-grid-row td' : {
56729                                 'vertical-align' : 0
56730                             },
56731                             '.course-edit-link' : {
56732                                 'color' : 'blue',
56733                                 'text-overflow' : 'ellipsis',
56734                                 'overflow' : 'hidden',
56735                                 'white-space' : 'nowrap',
56736                                 'cursor' : 'pointer'
56737                             },
56738                             '.sub-link' : {
56739                                 'color' : 'green'
56740                             },
56741                             '.de-act-sup-link' : {
56742                                 'color' : 'purple',
56743                                 'text-decoration' : 'line-through'
56744                             },
56745                             '.de-act-link' : {
56746                                 'color' : 'red',
56747                                 'text-decoration' : 'line-through'
56748                             },
56749                             '.course-timesheet .course-highlight' : {
56750                                 'border-top-style': 'dashed !important',
56751                                 'border-bottom-bottom': 'dashed !important'
56752                             },
56753                             '.course-timesheet .course-item' : {
56754                                 'font-family'   : 'tahoma, arial, helvetica',
56755                                 'font-size'     : '11px',
56756                                 'overflow'      : 'hidden',
56757                                 'padding-left'  : '10px',
56758                                 'padding-right' : '10px',
56759                                 'padding-top' : '10px' 
56760                             }
56761                             
56762                         }, Roo.id());
56763                                 this.ds.load({});
56764                     }
56765                 },
56766                 autoWidth : true,
56767                 monitorWindowResize : false,
56768                 cellrenderer : function(v,x,r)
56769                 {
56770                     return v;
56771                 },
56772                 sm : {
56773                     xtype: 'CellSelectionModel',
56774                     xns: Roo.grid
56775                 },
56776                 dataSource : {
56777                     xtype: 'Store',
56778                     xns: Roo.data,
56779                     listeners : {
56780                         beforeload : function (_self, options)
56781                         {
56782                             options.params = options.params || {};
56783                             options.params._month = _this.monthField.getValue();
56784                             options.params.limit = 9999;
56785                             options.params['sort'] = 'when_dt';    
56786                             options.params['dir'] = 'ASC';    
56787                             this.proxy.loadResponse = this.loadResponse;
56788                             Roo.log("load?");
56789                             //this.addColumns();
56790                         },
56791                         load : function (_self, records, options)
56792                         {
56793                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
56794                                 // if you click on the translation.. you can edit it...
56795                                 var el = Roo.get(this);
56796                                 var id = el.dom.getAttribute('data-id');
56797                                 var d = el.dom.getAttribute('data-date');
56798                                 var t = el.dom.getAttribute('data-time');
56799                                 //var id = this.child('span').dom.textContent;
56800                                 
56801                                 //Roo.log(this);
56802                                 Pman.Dialog.CourseCalendar.show({
56803                                     id : id,
56804                                     when_d : d,
56805                                     when_t : t,
56806                                     productitem_active : id ? 1 : 0
56807                                 }, function() {
56808                                     _this.grid.ds.load({});
56809                                 });
56810                            
56811                            });
56812                            
56813                            _this.panel.fireEvent('resize', [ '', '' ]);
56814                         }
56815                     },
56816                     loadResponse : function(o, success, response){
56817                             // this is overridden on before load..
56818                             
56819                             Roo.log("our code?");       
56820                             //Roo.log(success);
56821                             //Roo.log(response)
56822                             delete this.activeRequest;
56823                             if(!success){
56824                                 this.fireEvent("loadexception", this, o, response);
56825                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
56826                                 return;
56827                             }
56828                             var result;
56829                             try {
56830                                 result = o.reader.read(response);
56831                             }catch(e){
56832                                 Roo.log("load exception?");
56833                                 this.fireEvent("loadexception", this, o, response, e);
56834                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
56835                                 return;
56836                             }
56837                             Roo.log("ready...");        
56838                             // loop through result.records;
56839                             // and set this.tdate[date] = [] << array of records..
56840                             _this.tdata  = {};
56841                             Roo.each(result.records, function(r){
56842                                 //Roo.log(r.data);
56843                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
56844                                     _this.tdata[r.data.when_dt.format('j')] = [];
56845                                 }
56846                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
56847                             });
56848                             
56849                             //Roo.log(_this.tdata);
56850                             
56851                             result.records = [];
56852                             result.totalRecords = 6;
56853                     
56854                             // let's generate some duumy records for the rows.
56855                             //var st = _this.dateField.getValue();
56856                             
56857                             // work out monday..
56858                             //st = st.add(Date.DAY, -1 * st.format('w'));
56859                             
56860                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
56861                             
56862                             var firstOfMonth = date.getFirstDayOfMonth();
56863                             var days = date.getDaysInMonth();
56864                             var d = 1;
56865                             var firstAdded = false;
56866                             for (var i = 0; i < result.totalRecords ; i++) {
56867                                 //var d= st.add(Date.DAY, i);
56868                                 var row = {};
56869                                 var added = 0;
56870                                 for(var w = 0 ; w < 7 ; w++){
56871                                     if(!firstAdded && firstOfMonth != w){
56872                                         continue;
56873                                     }
56874                                     if(d > days){
56875                                         continue;
56876                                     }
56877                                     firstAdded = true;
56878                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
56879                                     row['weekday'+w] = String.format(
56880                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
56881                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
56882                                                     d,
56883                                                     date.format('Y-m-')+dd
56884                                                 );
56885                                     added++;
56886                                     if(typeof(_this.tdata[d]) != 'undefined'){
56887                                         Roo.each(_this.tdata[d], function(r){
56888                                             var is_sub = '';
56889                                             var deactive = '';
56890                                             var id = r.id;
56891                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
56892                                             if(r.parent_id*1>0){
56893                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
56894                                                 id = r.parent_id;
56895                                             }
56896                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
56897                                                 deactive = 'de-act-link';
56898                                             }
56899                                             
56900                                             row['weekday'+w] += String.format(
56901                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
56902                                                     id, //0
56903                                                     r.product_id_name, //1
56904                                                     r.when_dt.format('h:ia'), //2
56905                                                     is_sub, //3
56906                                                     deactive, //4
56907                                                     desc // 5
56908                                             );
56909                                         });
56910                                     }
56911                                     d++;
56912                                 }
56913                                 
56914                                 // only do this if something added..
56915                                 if(added > 0){ 
56916                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
56917                                 }
56918                                 
56919                                 
56920                                 // push it twice. (second one with an hour..
56921                                 
56922                             }
56923                             //Roo.log(result);
56924                             this.fireEvent("load", this, o, o.request.arg);
56925                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
56926                         },
56927                     sortInfo : {field: 'when_dt', direction : 'ASC' },
56928                     proxy : {
56929                         xtype: 'HttpProxy',
56930                         xns: Roo.data,
56931                         method : 'GET',
56932                         url : baseURL + '/Roo/Shop_course.php'
56933                     },
56934                     reader : {
56935                         xtype: 'JsonReader',
56936                         xns: Roo.data,
56937                         id : 'id',
56938                         fields : [
56939                             {
56940                                 'name': 'id',
56941                                 'type': 'int'
56942                             },
56943                             {
56944                                 'name': 'when_dt',
56945                                 'type': 'string'
56946                             },
56947                             {
56948                                 'name': 'end_dt',
56949                                 'type': 'string'
56950                             },
56951                             {
56952                                 'name': 'parent_id',
56953                                 'type': 'int'
56954                             },
56955                             {
56956                                 'name': 'product_id',
56957                                 'type': 'int'
56958                             },
56959                             {
56960                                 'name': 'productitem_id',
56961                                 'type': 'int'
56962                             },
56963                             {
56964                                 'name': 'guid',
56965                                 'type': 'int'
56966                             }
56967                         ]
56968                     }
56969                 },
56970                 toolbar : {
56971                     xtype: 'Toolbar',
56972                     xns: Roo,
56973                     items : [
56974                         {
56975                             xtype: 'Button',
56976                             xns: Roo.Toolbar,
56977                             listeners : {
56978                                 click : function (_self, e)
56979                                 {
56980                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
56981                                     sd.setMonth(sd.getMonth()-1);
56982                                     _this.monthField.setValue(sd.format('Y-m-d'));
56983                                     _this.grid.ds.load({});
56984                                 }
56985                             },
56986                             text : "Back"
56987                         },
56988                         {
56989                             xtype: 'Separator',
56990                             xns: Roo.Toolbar
56991                         },
56992                         {
56993                             xtype: 'MonthField',
56994                             xns: Roo.form,
56995                             listeners : {
56996                                 render : function (_self)
56997                                 {
56998                                     _this.monthField = _self;
56999                                    // _this.monthField.set  today
57000                                 },
57001                                 select : function (combo, date)
57002                                 {
57003                                     _this.grid.ds.load({});
57004                                 }
57005                             },
57006                             value : (function() { return new Date(); })()
57007                         },
57008                         {
57009                             xtype: 'Separator',
57010                             xns: Roo.Toolbar
57011                         },
57012                         {
57013                             xtype: 'TextItem',
57014                             xns: Roo.Toolbar,
57015                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57016                         },
57017                         {
57018                             xtype: 'Fill',
57019                             xns: Roo.Toolbar
57020                         },
57021                         {
57022                             xtype: 'Button',
57023                             xns: Roo.Toolbar,
57024                             listeners : {
57025                                 click : function (_self, e)
57026                                 {
57027                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57028                                     sd.setMonth(sd.getMonth()+1);
57029                                     _this.monthField.setValue(sd.format('Y-m-d'));
57030                                     _this.grid.ds.load({});
57031                                 }
57032                             },
57033                             text : "Next"
57034                         }
57035                     ]
57036                 },
57037                  
57038             }
57039         };
57040         
57041         *//*
57042  * Based on:
57043  * Ext JS Library 1.1.1
57044  * Copyright(c) 2006-2007, Ext JS, LLC.
57045  *
57046  * Originally Released Under LGPL - original licence link has changed is not relivant.
57047  *
57048  * Fork - LGPL
57049  * <script type="text/javascript">
57050  */
57051  
57052 /**
57053  * @class Roo.LoadMask
57054  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57055  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57056  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57057  * element's UpdateManager load indicator and will be destroyed after the initial load.
57058  * @constructor
57059  * Create a new LoadMask
57060  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57061  * @param {Object} config The config object
57062  */
57063 Roo.LoadMask = function(el, config){
57064     this.el = Roo.get(el);
57065     Roo.apply(this, config);
57066     if(this.store){
57067         this.store.on('beforeload', this.onBeforeLoad, this);
57068         this.store.on('load', this.onLoad, this);
57069         this.store.on('loadexception', this.onLoadException, this);
57070         this.removeMask = false;
57071     }else{
57072         var um = this.el.getUpdateManager();
57073         um.showLoadIndicator = false; // disable the default indicator
57074         um.on('beforeupdate', this.onBeforeLoad, this);
57075         um.on('update', this.onLoad, this);
57076         um.on('failure', this.onLoad, this);
57077         this.removeMask = true;
57078     }
57079 };
57080
57081 Roo.LoadMask.prototype = {
57082     /**
57083      * @cfg {Boolean} removeMask
57084      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57085      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57086      */
57087     /**
57088      * @cfg {String} msg
57089      * The text to display in a centered loading message box (defaults to 'Loading...')
57090      */
57091     msg : 'Loading...',
57092     /**
57093      * @cfg {String} msgCls
57094      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57095      */
57096     msgCls : 'x-mask-loading',
57097
57098     /**
57099      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57100      * @type Boolean
57101      */
57102     disabled: false,
57103
57104     /**
57105      * Disables the mask to prevent it from being displayed
57106      */
57107     disable : function(){
57108        this.disabled = true;
57109     },
57110
57111     /**
57112      * Enables the mask so that it can be displayed
57113      */
57114     enable : function(){
57115         this.disabled = false;
57116     },
57117     
57118     onLoadException : function()
57119     {
57120         Roo.log(arguments);
57121         
57122         if (typeof(arguments[3]) != 'undefined') {
57123             Roo.MessageBox.alert("Error loading",arguments[3]);
57124         } 
57125         /*
57126         try {
57127             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57128                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57129             }   
57130         } catch(e) {
57131             
57132         }
57133         */
57134     
57135         
57136         
57137         this.el.unmask(this.removeMask);
57138     },
57139     // private
57140     onLoad : function()
57141     {
57142         this.el.unmask(this.removeMask);
57143     },
57144
57145     // private
57146     onBeforeLoad : function(){
57147         if(!this.disabled){
57148             this.el.mask(this.msg, this.msgCls);
57149         }
57150     },
57151
57152     // private
57153     destroy : function(){
57154         if(this.store){
57155             this.store.un('beforeload', this.onBeforeLoad, this);
57156             this.store.un('load', this.onLoad, this);
57157             this.store.un('loadexception', this.onLoadException, this);
57158         }else{
57159             var um = this.el.getUpdateManager();
57160             um.un('beforeupdate', this.onBeforeLoad, this);
57161             um.un('update', this.onLoad, this);
57162             um.un('failure', this.onLoad, this);
57163         }
57164     }
57165 };/*
57166  * Based on:
57167  * Ext JS Library 1.1.1
57168  * Copyright(c) 2006-2007, Ext JS, LLC.
57169  *
57170  * Originally Released Under LGPL - original licence link has changed is not relivant.
57171  *
57172  * Fork - LGPL
57173  * <script type="text/javascript">
57174  */
57175
57176
57177 /**
57178  * @class Roo.XTemplate
57179  * @extends Roo.Template
57180  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57181 <pre><code>
57182 var t = new Roo.XTemplate(
57183         '&lt;select name="{name}"&gt;',
57184                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57185         '&lt;/select&gt;'
57186 );
57187  
57188 // then append, applying the master template values
57189  </code></pre>
57190  *
57191  * Supported features:
57192  *
57193  *  Tags:
57194
57195 <pre><code>
57196       {a_variable} - output encoded.
57197       {a_variable.format:("Y-m-d")} - call a method on the variable
57198       {a_variable:raw} - unencoded output
57199       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57200       {a_variable:this.method_on_template(...)} - call a method on the template object.
57201  
57202 </code></pre>
57203  *  The tpl tag:
57204 <pre><code>
57205         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57206         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57207         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57208         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57209   
57210         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57211         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57212 </code></pre>
57213  *      
57214  */
57215 Roo.XTemplate = function()
57216 {
57217     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57218     if (this.html) {
57219         this.compile();
57220     }
57221 };
57222
57223
57224 Roo.extend(Roo.XTemplate, Roo.Template, {
57225
57226     /**
57227      * The various sub templates
57228      */
57229     tpls : false,
57230     /**
57231      *
57232      * basic tag replacing syntax
57233      * WORD:WORD()
57234      *
57235      * // you can fake an object call by doing this
57236      *  x.t:(test,tesT) 
57237      * 
57238      */
57239     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57240
57241     /**
57242      * compile the template
57243      *
57244      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57245      *
57246      */
57247     compile: function()
57248     {
57249         var s = this.html;
57250      
57251         s = ['<tpl>', s, '</tpl>'].join('');
57252     
57253         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57254             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57255             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57256             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57257             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57258             m,
57259             id     = 0,
57260             tpls   = [];
57261     
57262         while(true == !!(m = s.match(re))){
57263             var forMatch   = m[0].match(nameRe),
57264                 ifMatch   = m[0].match(ifRe),
57265                 execMatch   = m[0].match(execRe),
57266                 namedMatch   = m[0].match(namedRe),
57267                 
57268                 exp  = null, 
57269                 fn   = null,
57270                 exec = null,
57271                 name = forMatch && forMatch[1] ? forMatch[1] : '';
57272                 
57273             if (ifMatch) {
57274                 // if - puts fn into test..
57275                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
57276                 if(exp){
57277                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
57278                 }
57279             }
57280             
57281             if (execMatch) {
57282                 // exec - calls a function... returns empty if true is  returned.
57283                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
57284                 if(exp){
57285                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
57286                 }
57287             }
57288             
57289             
57290             if (name) {
57291                 // for = 
57292                 switch(name){
57293                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
57294                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
57295                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
57296                 }
57297             }
57298             var uid = namedMatch ? namedMatch[1] : id;
57299             
57300             
57301             tpls.push({
57302                 id:     namedMatch ? namedMatch[1] : id,
57303                 target: name,
57304                 exec:   exec,
57305                 test:   fn,
57306                 body:   m[1] || ''
57307             });
57308             if (namedMatch) {
57309                 s = s.replace(m[0], '');
57310             } else { 
57311                 s = s.replace(m[0], '{xtpl'+ id + '}');
57312             }
57313             ++id;
57314         }
57315         this.tpls = [];
57316         for(var i = tpls.length-1; i >= 0; --i){
57317             this.compileTpl(tpls[i]);
57318             this.tpls[tpls[i].id] = tpls[i];
57319         }
57320         this.master = tpls[tpls.length-1];
57321         return this;
57322     },
57323     /**
57324      * same as applyTemplate, except it's done to one of the subTemplates
57325      * when using named templates, you can do:
57326      *
57327      * var str = pl.applySubTemplate('your-name', values);
57328      *
57329      * 
57330      * @param {Number} id of the template
57331      * @param {Object} values to apply to template
57332      * @param {Object} parent (normaly the instance of this object)
57333      */
57334     applySubTemplate : function(id, values, parent)
57335     {
57336         
57337         
57338         var t = this.tpls[id];
57339         
57340         
57341         try { 
57342             if(t.test && !t.test.call(this, values, parent)){
57343                 return '';
57344             }
57345         } catch(e) {
57346             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
57347             Roo.log(e.toString());
57348             Roo.log(t.test);
57349             return ''
57350         }
57351         try { 
57352             
57353             if(t.exec && t.exec.call(this, values, parent)){
57354                 return '';
57355             }
57356         } catch(e) {
57357             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
57358             Roo.log(e.toString());
57359             Roo.log(t.exec);
57360             return ''
57361         }
57362         try {
57363             var vs = t.target ? t.target.call(this, values, parent) : values;
57364             parent = t.target ? values : parent;
57365             if(t.target && vs instanceof Array){
57366                 var buf = [];
57367                 for(var i = 0, len = vs.length; i < len; i++){
57368                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
57369                 }
57370                 return buf.join('');
57371             }
57372             return t.compiled.call(this, vs, parent);
57373         } catch (e) {
57374             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
57375             Roo.log(e.toString());
57376             Roo.log(t.compiled);
57377             return '';
57378         }
57379     },
57380
57381     compileTpl : function(tpl)
57382     {
57383         var fm = Roo.util.Format;
57384         var useF = this.disableFormats !== true;
57385         var sep = Roo.isGecko ? "+" : ",";
57386         var undef = function(str) {
57387             Roo.log("Property not found :"  + str);
57388             return '';
57389         };
57390         
57391         var fn = function(m, name, format, args)
57392         {
57393             //Roo.log(arguments);
57394             args = args ? args.replace(/\\'/g,"'") : args;
57395             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
57396             if (typeof(format) == 'undefined') {
57397                 format= 'htmlEncode';
57398             }
57399             if (format == 'raw' ) {
57400                 format = false;
57401             }
57402             
57403             if(name.substr(0, 4) == 'xtpl'){
57404                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
57405             }
57406             
57407             // build an array of options to determine if value is undefined..
57408             
57409             // basically get 'xxxx.yyyy' then do
57410             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
57411             //    (function () { Roo.log("Property not found"); return ''; })() :
57412             //    ......
57413             
57414             var udef_ar = [];
57415             var lookfor = '';
57416             Roo.each(name.split('.'), function(st) {
57417                 lookfor += (lookfor.length ? '.': '') + st;
57418                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
57419             });
57420             
57421             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
57422             
57423             
57424             if(format && useF){
57425                 
57426                 args = args ? ',' + args : "";
57427                  
57428                 if(format.substr(0, 5) != "this."){
57429                     format = "fm." + format + '(';
57430                 }else{
57431                     format = 'this.call("'+ format.substr(5) + '", ';
57432                     args = ", values";
57433                 }
57434                 
57435                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
57436             }
57437              
57438             if (args.length) {
57439                 // called with xxyx.yuu:(test,test)
57440                 // change to ()
57441                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
57442             }
57443             // raw.. - :raw modifier..
57444             return "'"+ sep + udef_st  + name + ")"+sep+"'";
57445             
57446         };
57447         var body;
57448         // branched to use + in gecko and [].join() in others
57449         if(Roo.isGecko){
57450             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
57451                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
57452                     "';};};";
57453         }else{
57454             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
57455             body.push(tpl.body.replace(/(\r\n|\n)/g,
57456                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
57457             body.push("'].join('');};};");
57458             body = body.join('');
57459         }
57460         
57461         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
57462        
57463         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
57464         eval(body);
57465         
57466         return this;
57467     },
57468
57469     applyTemplate : function(values){
57470         return this.master.compiled.call(this, values, {});
57471         //var s = this.subs;
57472     },
57473
57474     apply : function(){
57475         return this.applyTemplate.apply(this, arguments);
57476     }
57477
57478  });
57479
57480 Roo.XTemplate.from = function(el){
57481     el = Roo.getDom(el);
57482     return new Roo.XTemplate(el.value || el.innerHTML);
57483 };