roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4848 <p>
4849 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4850
4851 <p>
4852 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String} name The attribute name
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (!this.dom.hasAttribute(name)) {
9582                 return undefined;
9583             }
9584             return this.dom.getAttribute(name);
9585         }
9586         
9587         
9588         
9589     };
9590
9591     var ep = El.prototype;
9592
9593     /**
9594      * Appends an event handler (Shorthand for addListener)
9595      * @param {String}   eventName     The type of event to append
9596      * @param {Function} fn        The method the event invokes
9597      * @param {Object} scope       (optional) The scope (this object) of the fn
9598      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9599      * @method
9600      */
9601     ep.on = ep.addListener;
9602         // backwards compat
9603     ep.mon = ep.addListener;
9604
9605     /**
9606      * Removes an event handler from this element (shorthand for removeListener)
9607      * @param {String} eventName the type of event to remove
9608      * @param {Function} fn the method the event invokes
9609      * @return {Roo.Element} this
9610      * @method
9611      */
9612     ep.un = ep.removeListener;
9613
9614     /**
9615      * true to automatically adjust width and height settings for box-model issues (default to true)
9616      */
9617     ep.autoBoxAdjust = true;
9618
9619     // private
9620     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9621
9622     // private
9623     El.addUnits = function(v, defaultUnit){
9624         if(v === "" || v == "auto"){
9625             return v;
9626         }
9627         if(v === undefined){
9628             return '';
9629         }
9630         if(typeof v == "number" || !El.unitPattern.test(v)){
9631             return v + (defaultUnit || 'px');
9632         }
9633         return v;
9634     };
9635
9636     // special markup used throughout Roo when box wrapping elements
9637     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9638     /**
9639      * Visibility mode constant - Use visibility to hide element
9640      * @static
9641      * @type Number
9642      */
9643     El.VISIBILITY = 1;
9644     /**
9645      * Visibility mode constant - Use display to hide element
9646      * @static
9647      * @type Number
9648      */
9649     El.DISPLAY = 2;
9650
9651     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9652     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9653     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9654
9655
9656
9657     /**
9658      * @private
9659      */
9660     El.cache = {};
9661
9662     var docEl;
9663
9664     /**
9665      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9666      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9667      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9668      * @return {Element} The Element object
9669      * @static
9670      */
9671     El.get = function(el){
9672         var ex, elm, id;
9673         if(!el){ return null; }
9674         if(typeof el == "string"){ // element id
9675             if(!(elm = document.getElementById(el))){
9676                 return null;
9677             }
9678             if(ex = El.cache[el]){
9679                 ex.dom = elm;
9680             }else{
9681                 ex = El.cache[el] = new El(elm);
9682             }
9683             return ex;
9684         }else if(el.tagName){ // dom element
9685             if(!(id = el.id)){
9686                 id = Roo.id(el);
9687             }
9688             if(ex = El.cache[id]){
9689                 ex.dom = el;
9690             }else{
9691                 ex = El.cache[id] = new El(el);
9692             }
9693             return ex;
9694         }else if(el instanceof El){
9695             if(el != docEl){
9696                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9697                                                               // catch case where it hasn't been appended
9698                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9699             }
9700             return el;
9701         }else if(el.isComposite){
9702             return el;
9703         }else if(el instanceof Array){
9704             return El.select(el);
9705         }else if(el == document){
9706             // create a bogus element object representing the document object
9707             if(!docEl){
9708                 var f = function(){};
9709                 f.prototype = El.prototype;
9710                 docEl = new f();
9711                 docEl.dom = document;
9712             }
9713             return docEl;
9714         }
9715         return null;
9716     };
9717
9718     // private
9719     El.uncache = function(el){
9720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9721             if(a[i]){
9722                 delete El.cache[a[i].id || a[i]];
9723             }
9724         }
9725     };
9726
9727     // private
9728     // Garbage collection - uncache elements/purge listeners on orphaned elements
9729     // so we don't hold a reference and cause the browser to retain them
9730     El.garbageCollect = function(){
9731         if(!Roo.enableGarbageCollector){
9732             clearInterval(El.collectorThread);
9733             return;
9734         }
9735         for(var eid in El.cache){
9736             var el = El.cache[eid], d = el.dom;
9737             // -------------------------------------------------------
9738             // Determining what is garbage:
9739             // -------------------------------------------------------
9740             // !d
9741             // dom node is null, definitely garbage
9742             // -------------------------------------------------------
9743             // !d.parentNode
9744             // no parentNode == direct orphan, definitely garbage
9745             // -------------------------------------------------------
9746             // !d.offsetParent && !document.getElementById(eid)
9747             // display none elements have no offsetParent so we will
9748             // also try to look it up by it's id. However, check
9749             // offsetParent first so we don't do unneeded lookups.
9750             // This enables collection of elements that are not orphans
9751             // directly, but somewhere up the line they have an orphan
9752             // parent.
9753             // -------------------------------------------------------
9754             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9755                 delete El.cache[eid];
9756                 if(d && Roo.enableListenerCollection){
9757                     E.purgeElement(d);
9758                 }
9759             }
9760         }
9761     }
9762     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9763
9764
9765     // dom is optional
9766     El.Flyweight = function(dom){
9767         this.dom = dom;
9768     };
9769     El.Flyweight.prototype = El.prototype;
9770
9771     El._flyweights = {};
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * @param {String/HTMLElement} el The dom node or id
9776      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9777      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9778      * @static
9779      * @return {Element} The shared Element object
9780      */
9781     El.fly = function(el, named){
9782         named = named || '_global';
9783         el = Roo.getDom(el);
9784         if(!el){
9785             return null;
9786         }
9787         if(!El._flyweights[named]){
9788             El._flyweights[named] = new El.Flyweight();
9789         }
9790         El._flyweights[named].dom = el;
9791         return El._flyweights[named];
9792     };
9793
9794     /**
9795      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9796      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9797      * Shorthand of {@link Roo.Element#get}
9798      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9799      * @return {Element} The Element object
9800      * @member Roo
9801      * @method get
9802      */
9803     Roo.get = El.get;
9804     /**
9805      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9806      * the dom node can be overwritten by other code.
9807      * Shorthand of {@link Roo.Element#fly}
9808      * @param {String/HTMLElement} el The dom node or id
9809      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9810      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9811      * @static
9812      * @return {Element} The shared Element object
9813      * @member Roo
9814      * @method fly
9815      */
9816     Roo.fly = El.fly;
9817
9818     // speedy lookup for elements never to box adjust
9819     var noBoxAdjust = Roo.isStrict ? {
9820         select:1
9821     } : {
9822         input:1, select:1, textarea:1
9823     };
9824     if(Roo.isIE || Roo.isGecko){
9825         noBoxAdjust['button'] = 1;
9826     }
9827
9828
9829     Roo.EventManager.on(window, 'unload', function(){
9830         delete El.cache;
9831         delete El._flyweights;
9832     });
9833 })();
9834
9835
9836
9837
9838 if(Roo.DomQuery){
9839     Roo.Element.selectorFunction = Roo.DomQuery.select;
9840 }
9841
9842 Roo.Element.select = function(selector, unique, root){
9843     var els;
9844     if(typeof selector == "string"){
9845         els = Roo.Element.selectorFunction(selector, root);
9846     }else if(selector.length !== undefined){
9847         els = selector;
9848     }else{
9849         throw "Invalid selector";
9850     }
9851     if(unique === true){
9852         return new Roo.CompositeElement(els);
9853     }else{
9854         return new Roo.CompositeElementLite(els);
9855     }
9856 };
9857 /**
9858  * Selects elements based on the passed CSS selector to enable working on them as 1.
9859  * @param {String/Array} selector The CSS selector or an array of elements
9860  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9861  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9862  * @return {CompositeElementLite/CompositeElement}
9863  * @member Roo
9864  * @method select
9865  */
9866 Roo.select = Roo.Element.select;
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881 /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893
9894 //Notifies Element that fx methods are available
9895 Roo.enableFx = true;
9896
9897 /**
9898  * @class Roo.Fx
9899  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9900  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9901  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9902  * Element effects to work.</p><br/>
9903  *
9904  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9905  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9906  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9907  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9908  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9909  * expected results and should be done with care.</p><br/>
9910  *
9911  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9912  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9913 <pre>
9914 Value  Description
9915 -----  -----------------------------
9916 tl     The top left corner
9917 t      The center of the top edge
9918 tr     The top right corner
9919 l      The center of the left edge
9920 r      The center of the right edge
9921 bl     The bottom left corner
9922 b      The center of the bottom edge
9923 br     The bottom right corner
9924 </pre>
9925  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9926  * below are common options that can be passed to any Fx method.</b>
9927  * @cfg {Function} callback A function called when the effect is finished
9928  * @cfg {Object} scope The scope of the effect function
9929  * @cfg {String} easing A valid Easing value for the effect
9930  * @cfg {String} afterCls A css class to apply after the effect
9931  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9932  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9933  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9934  * effects that end with the element being visually hidden, ignored otherwise)
9935  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9936  * a function which returns such a specification that will be applied to the Element after the effect finishes
9937  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9938  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9939  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9940  */
9941 Roo.Fx = {
9942         /**
9943          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9944          * origin for the slide effect.  This function automatically handles wrapping the element with
9945          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element in from the top
9949 el.slideIn();
9950
9951 // custom: slide the element in from the right with a 2-second duration
9952 el.slideIn('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideIn('t', {
9956     easing: 'easeOut',
9957     duration: .5
9958 });
9959 </code></pre>
9960          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9961          * @param {Object} options (optional) Object literal with any of the Fx config options
9962          * @return {Roo.Element} The Element
9963          */
9964     slideIn : function(anchor, o){
9965         var el = this.getFxEl();
9966         o = o || {};
9967
9968         el.queueFx(o, function(){
9969
9970             anchor = anchor || "t";
9971
9972             // fix display to visibility
9973             this.fixDisplay();
9974
9975             // restore values after effect
9976             var r = this.getFxRestore();
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "hidden");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             // clear out temp styles after slide and unwrap
9989             var after = function(){
9990                 el.fxUnwrap(wrap, r.pos, o);
9991                 st.width = r.width;
9992                 st.height = r.height;
9993                 el.afterFx(o);
9994             };
9995             // time to calc the positions
9996             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9997
9998             switch(anchor.toLowerCase()){
9999                 case "t":
10000                     wrap.setSize(b.width, 0);
10001                     st.left = st.bottom = "0";
10002                     a = {height: bh};
10003                 break;
10004                 case "l":
10005                     wrap.setSize(0, b.height);
10006                     st.right = st.top = "0";
10007                     a = {width: bw};
10008                 break;
10009                 case "r":
10010                     wrap.setSize(0, b.height);
10011                     wrap.setX(b.right);
10012                     st.left = st.top = "0";
10013                     a = {width: bw, points: pt};
10014                 break;
10015                 case "b":
10016                     wrap.setSize(b.width, 0);
10017                     wrap.setY(b.bottom);
10018                     st.left = st.top = "0";
10019                     a = {height: bh, points: pt};
10020                 break;
10021                 case "tl":
10022                     wrap.setSize(0, 0);
10023                     st.right = st.bottom = "0";
10024                     a = {width: bw, height: bh};
10025                 break;
10026                 case "bl":
10027                     wrap.setSize(0, 0);
10028                     wrap.setY(b.y+b.height);
10029                     st.right = st.top = "0";
10030                     a = {width: bw, height: bh, points: pt};
10031                 break;
10032                 case "br":
10033                     wrap.setSize(0, 0);
10034                     wrap.setXY([b.right, b.bottom]);
10035                     st.left = st.top = "0";
10036                     a = {width: bw, height: bh, points: pt};
10037                 break;
10038                 case "tr":
10039                     wrap.setSize(0, 0);
10040                     wrap.setX(b.x+b.width);
10041                     st.left = st.bottom = "0";
10042                     a = {width: bw, height: bh, points: pt};
10043                 break;
10044             }
10045             this.dom.style.visibility = "visible";
10046             wrap.show();
10047
10048             arguments.callee.anim = wrap.fxanim(a,
10049                 o,
10050                 'motion',
10051                 .5,
10052                 'easeOut', after);
10053         });
10054         return this;
10055     },
10056     
10057         /**
10058          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10059          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10060          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10061          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10062          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10063          * Usage:
10064          *<pre><code>
10065 // default: slide the element out to the top
10066 el.slideOut();
10067
10068 // custom: slide the element out to the right with a 2-second duration
10069 el.slideOut('r', { duration: 2 });
10070
10071 // common config options shown with default values
10072 el.slideOut('t', {
10073     easing: 'easeOut',
10074     duration: .5,
10075     remove: false,
10076     useDisplay: false
10077 });
10078 </code></pre>
10079          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10080          * @param {Object} options (optional) Object literal with any of the Fx config options
10081          * @return {Roo.Element} The Element
10082          */
10083     slideOut : function(anchor, o){
10084         var el = this.getFxEl();
10085         o = o || {};
10086
10087         el.queueFx(o, function(){
10088
10089             anchor = anchor || "t";
10090
10091             // restore values after effect
10092             var r = this.getFxRestore();
10093             
10094             var b = this.getBox();
10095             // fixed size for slide
10096             this.setSize(b);
10097
10098             // wrap if needed
10099             var wrap = this.fxWrap(r.pos, o, "visible");
10100
10101             var st = this.dom.style;
10102             st.visibility = "visible";
10103             st.position = "absolute";
10104
10105             wrap.setSize(b);
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.fxUnwrap(wrap, r.pos, o);
10115
10116                 st.width = r.width;
10117                 st.height = r.height;
10118
10119                 el.afterFx(o);
10120             };
10121
10122             var a, zero = {to: 0};
10123             switch(anchor.toLowerCase()){
10124                 case "t":
10125                     st.left = st.bottom = "0";
10126                     a = {height: zero};
10127                 break;
10128                 case "l":
10129                     st.right = st.top = "0";
10130                     a = {width: zero};
10131                 break;
10132                 case "r":
10133                     st.left = st.top = "0";
10134                     a = {width: zero, points: {to:[b.right, b.y]}};
10135                 break;
10136                 case "b":
10137                     st.left = st.top = "0";
10138                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "tl":
10141                     st.right = st.bottom = "0";
10142                     a = {width: zero, height: zero};
10143                 break;
10144                 case "bl":
10145                     st.right = st.top = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "br":
10149                     st.left = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10151                 break;
10152                 case "tr":
10153                     st.left = st.bottom = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10155                 break;
10156             }
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 "easeOut", after);
10163         });
10164         return this;
10165     },
10166
10167         /**
10168          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10169          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10170          * The element must be removed from the DOM using the 'remove' config option if desired.
10171          * Usage:
10172          *<pre><code>
10173 // default
10174 el.puff();
10175
10176 // common config options shown with default values
10177 el.puff({
10178     easing: 'easeOut',
10179     duration: .5,
10180     remove: false,
10181     useDisplay: false
10182 });
10183 </code></pre>
10184          * @param {Object} options (optional) Object literal with any of the Fx config options
10185          * @return {Roo.Element} The Element
10186          */
10187     puff : function(o){
10188         var el = this.getFxEl();
10189         o = o || {};
10190
10191         el.queueFx(o, function(){
10192             this.clearOpacity();
10193             this.show();
10194
10195             // restore values after effect
10196             var r = this.getFxRestore();
10197             var st = this.dom.style;
10198
10199             var after = function(){
10200                 if(o.useDisplay){
10201                     el.setDisplayed(false);
10202                 }else{
10203                     el.hide();
10204                 }
10205
10206                 el.clearOpacity();
10207
10208                 el.setPositioning(r.pos);
10209                 st.width = r.width;
10210                 st.height = r.height;
10211                 st.fontSize = '';
10212                 el.afterFx(o);
10213             };
10214
10215             var width = this.getWidth();
10216             var height = this.getHeight();
10217
10218             arguments.callee.anim = this.fxanim({
10219                     width : {to: this.adjustWidth(width * 2)},
10220                     height : {to: this.adjustHeight(height * 2)},
10221                     points : {by: [-(width * .5), -(height * .5)]},
10222                     opacity : {to: 0},
10223                     fontSize: {to:200, unit: "%"}
10224                 },
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10235          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10236          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.switchOff();
10241
10242 // all config options shown with default values
10243 el.switchOff({
10244     easing: 'easeIn',
10245     duration: .3,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     switchOff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.clip();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273                 el.setPositioning(r.pos);
10274                 st.width = r.width;
10275                 st.height = r.height;
10276
10277                 el.afterFx(o);
10278             };
10279
10280             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10281                 this.clearOpacity();
10282                 (function(){
10283                     this.fxanim({
10284                         height:{to:1},
10285                         points:{by:[0, this.getHeight() * .5]}
10286                     }, o, 'motion', 0.3, 'easeIn', after);
10287                 }).defer(100, this);
10288             });
10289         });
10290         return this;
10291     },
10292
10293     /**
10294      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10295      * changed using the "attr" config option) and then fading back to the original color. If no original
10296      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10297      * Usage:
10298 <pre><code>
10299 // default: highlight background to yellow
10300 el.highlight();
10301
10302 // custom: highlight foreground text to blue for 2 seconds
10303 el.highlight("0000ff", { attr: 'color', duration: 2 });
10304
10305 // common config options shown with default values
10306 el.highlight("ffff9c", {
10307     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10308     endColor: (current color) or "ffffff",
10309     easing: 'easeIn',
10310     duration: 1
10311 });
10312 </code></pre>
10313      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10314      * @param {Object} options (optional) Object literal with any of the Fx config options
10315      * @return {Roo.Element} The Element
10316      */ 
10317     highlight : function(color, o){
10318         var el = this.getFxEl();
10319         o = o || {};
10320
10321         el.queueFx(o, function(){
10322             color = color || "ffff9c";
10323             attr = o.attr || "backgroundColor";
10324
10325             this.clearOpacity();
10326             this.show();
10327
10328             var origColor = this.getColor(attr);
10329             var restoreColor = this.dom.style[attr];
10330             endColor = (o.endColor || origColor) || "ffffff";
10331
10332             var after = function(){
10333                 el.dom.style[attr] = restoreColor;
10334                 el.afterFx(o);
10335             };
10336
10337             var a = {};
10338             a[attr] = {from: color, to: endColor};
10339             arguments.callee.anim = this.fxanim(a,
10340                 o,
10341                 'color',
10342                 1,
10343                 'easeIn', after);
10344         });
10345         return this;
10346     },
10347
10348    /**
10349     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10350     * Usage:
10351 <pre><code>
10352 // default: a single light blue ripple
10353 el.frame();
10354
10355 // custom: 3 red ripples lasting 3 seconds total
10356 el.frame("ff0000", 3, { duration: 3 });
10357
10358 // common config options shown with default values
10359 el.frame("C3DAF9", 1, {
10360     duration: 1 //duration of entire animation (not each individual ripple)
10361     // Note: Easing is not configurable and will be ignored if included
10362 });
10363 </code></pre>
10364     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10365     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10366     * @param {Object} options (optional) Object literal with any of the Fx config options
10367     * @return {Roo.Element} The Element
10368     */
10369     frame : function(color, count, o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             color = color || "#C3DAF9";
10375             if(color.length == 6){
10376                 color = "#" + color;
10377             }
10378             count = count || 1;
10379             duration = o.duration || 1;
10380             this.show();
10381
10382             var b = this.getBox();
10383             var animFn = function(){
10384                 var proxy = this.createProxy({
10385
10386                      style:{
10387                         visbility:"hidden",
10388                         position:"absolute",
10389                         "z-index":"35000", // yee haw
10390                         border:"0px solid " + color
10391                      }
10392                   });
10393                 var scale = Roo.isBorderBox ? 2 : 1;
10394                 proxy.animate({
10395                     top:{from:b.y, to:b.y - 20},
10396                     left:{from:b.x, to:b.x - 20},
10397                     borderWidth:{from:0, to:10},
10398                     opacity:{from:1, to:0},
10399                     height:{from:b.height, to:(b.height + (20*scale))},
10400                     width:{from:b.width, to:(b.width + (20*scale))}
10401                 }, duration, function(){
10402                     proxy.remove();
10403                 });
10404                 if(--count > 0){
10405                      animFn.defer((duration/2)*1000, this);
10406                 }else{
10407                     el.afterFx(o);
10408                 }
10409             };
10410             animFn.call(this);
10411         });
10412         return this;
10413     },
10414
10415    /**
10416     * Creates a pause before any subsequent queued effects begin.  If there are
10417     * no effects queued after the pause it will have no effect.
10418     * Usage:
10419 <pre><code>
10420 el.pause(1);
10421 </code></pre>
10422     * @param {Number} seconds The length of time to pause (in seconds)
10423     * @return {Roo.Element} The Element
10424     */
10425     pause : function(seconds){
10426         var el = this.getFxEl();
10427         var o = {};
10428
10429         el.queueFx(o, function(){
10430             setTimeout(function(){
10431                 el.afterFx(o);
10432             }, seconds * 1000);
10433         });
10434         return this;
10435     },
10436
10437    /**
10438     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10439     * using the "endOpacity" config option.
10440     * Usage:
10441 <pre><code>
10442 // default: fade in from opacity 0 to 100%
10443 el.fadeIn();
10444
10445 // custom: fade in from opacity 0 to 75% over 2 seconds
10446 el.fadeIn({ endOpacity: .75, duration: 2});
10447
10448 // common config options shown with default values
10449 el.fadeIn({
10450     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10451     easing: 'easeOut',
10452     duration: .5
10453 });
10454 </code></pre>
10455     * @param {Object} options (optional) Object literal with any of the Fx config options
10456     * @return {Roo.Element} The Element
10457     */
10458     fadeIn : function(o){
10459         var el = this.getFxEl();
10460         o = o || {};
10461         el.queueFx(o, function(){
10462             this.setOpacity(0);
10463             this.fixDisplay();
10464             this.dom.style.visibility = 'visible';
10465             var to = o.endOpacity || 1;
10466             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10467                 o, null, .5, "easeOut", function(){
10468                 if(to == 1){
10469                     this.clearOpacity();
10470                 }
10471                 el.afterFx(o);
10472             });
10473         });
10474         return this;
10475     },
10476
10477    /**
10478     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10479     * using the "endOpacity" config option.
10480     * Usage:
10481 <pre><code>
10482 // default: fade out from the element's current opacity to 0
10483 el.fadeOut();
10484
10485 // custom: fade out from the element's current opacity to 25% over 2 seconds
10486 el.fadeOut({ endOpacity: .25, duration: 2});
10487
10488 // common config options shown with default values
10489 el.fadeOut({
10490     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10491     easing: 'easeOut',
10492     duration: .5
10493     remove: false,
10494     useDisplay: false
10495 });
10496 </code></pre>
10497     * @param {Object} options (optional) Object literal with any of the Fx config options
10498     * @return {Roo.Element} The Element
10499     */
10500     fadeOut : function(o){
10501         var el = this.getFxEl();
10502         o = o || {};
10503         el.queueFx(o, function(){
10504             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10505                 o, null, .5, "easeOut", function(){
10506                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10507                      this.dom.style.display = "none";
10508                 }else{
10509                      this.dom.style.visibility = "hidden";
10510                 }
10511                 this.clearOpacity();
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Animates the transition of an element's dimensions from a starting height/width
10520     * to an ending height/width.
10521     * Usage:
10522 <pre><code>
10523 // change height and width to 100x100 pixels
10524 el.scale(100, 100);
10525
10526 // common config options shown with default values.  The height and width will default to
10527 // the element's existing values if passed as null.
10528 el.scale(
10529     [element's width],
10530     [element's height], {
10531     easing: 'easeOut',
10532     duration: .35
10533 });
10534 </code></pre>
10535     * @param {Number} width  The new width (pass undefined to keep the original width)
10536     * @param {Number} height  The new height (pass undefined to keep the original height)
10537     * @param {Object} options (optional) Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     scale : function(w, h, o){
10541         this.shift(Roo.apply({}, o, {
10542             width: w,
10543             height: h
10544         }));
10545         return this;
10546     },
10547
10548    /**
10549     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10550     * Any of these properties not specified in the config object will not be changed.  This effect 
10551     * requires that at least one new dimension, position or opacity setting must be passed in on
10552     * the config object in order for the function to have any effect.
10553     * Usage:
10554 <pre><code>
10555 // slide the element horizontally to x position 200 while changing the height and opacity
10556 el.shift({ x: 200, height: 50, opacity: .8 });
10557
10558 // common config options shown with default values.
10559 el.shift({
10560     width: [element's width],
10561     height: [element's height],
10562     x: [element's x position],
10563     y: [element's y position],
10564     opacity: [element's opacity],
10565     easing: 'easeOut',
10566     duration: .35
10567 });
10568 </code></pre>
10569     * @param {Object} options  Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     shift : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10577             if(w !== undefined){
10578                 a.width = {to: this.adjustWidth(w)};
10579             }
10580             if(h !== undefined){
10581                 a.height = {to: this.adjustHeight(h)};
10582             }
10583             if(x !== undefined || y !== undefined){
10584                 a.points = {to: [
10585                     x !== undefined ? x : this.getX(),
10586                     y !== undefined ? y : this.getY()
10587                 ]};
10588             }
10589             if(op !== undefined){
10590                 a.opacity = {to: op};
10591             }
10592             if(o.xy !== undefined){
10593                 a.points = {to: o.xy};
10594             }
10595             arguments.callee.anim = this.fxanim(a,
10596                 o, 'motion', .35, "easeOut", function(){
10597                 el.afterFx(o);
10598             });
10599         });
10600         return this;
10601     },
10602
10603         /**
10604          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10605          * ending point of the effect.
10606          * Usage:
10607          *<pre><code>
10608 // default: slide the element downward while fading out
10609 el.ghost();
10610
10611 // custom: slide the element out to the right with a 2-second duration
10612 el.ghost('r', { duration: 2 });
10613
10614 // common config options shown with default values
10615 el.ghost('b', {
10616     easing: 'easeOut',
10617     duration: .5
10618     remove: false,
10619     useDisplay: false
10620 });
10621 </code></pre>
10622          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10623          * @param {Object} options (optional) Object literal with any of the Fx config options
10624          * @return {Roo.Element} The Element
10625          */
10626     ghost : function(anchor, o){
10627         var el = this.getFxEl();
10628         o = o || {};
10629
10630         el.queueFx(o, function(){
10631             anchor = anchor || "b";
10632
10633             // restore values after effect
10634             var r = this.getFxRestore();
10635             var w = this.getWidth(),
10636                 h = this.getHeight();
10637
10638             var st = this.dom.style;
10639
10640             var after = function(){
10641                 if(o.useDisplay){
10642                     el.setDisplayed(false);
10643                 }else{
10644                     el.hide();
10645                 }
10646
10647                 el.clearOpacity();
10648                 el.setPositioning(r.pos);
10649                 st.width = r.width;
10650                 st.height = r.height;
10651
10652                 el.afterFx(o);
10653             };
10654
10655             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10656             switch(anchor.toLowerCase()){
10657                 case "t":
10658                     pt.by = [0, -h];
10659                 break;
10660                 case "l":
10661                     pt.by = [-w, 0];
10662                 break;
10663                 case "r":
10664                     pt.by = [w, 0];
10665                 break;
10666                 case "b":
10667                     pt.by = [0, h];
10668                 break;
10669                 case "tl":
10670                     pt.by = [-w, -h];
10671                 break;
10672                 case "bl":
10673                     pt.by = [-w, h];
10674                 break;
10675                 case "br":
10676                     pt.by = [w, h];
10677                 break;
10678                 case "tr":
10679                     pt.by = [w, -h];
10680                 break;
10681             }
10682
10683             arguments.callee.anim = this.fxanim(a,
10684                 o,
10685                 'motion',
10686                 .5,
10687                 "easeOut", after);
10688         });
10689         return this;
10690     },
10691
10692         /**
10693          * Ensures that all effects queued after syncFx is called on the element are
10694          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10695          * @return {Roo.Element} The Element
10696          */
10697     syncFx : function(){
10698         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10699             block : false,
10700             concurrent : true,
10701             stopFx : false
10702         });
10703         return this;
10704     },
10705
10706         /**
10707          * Ensures that all effects queued after sequenceFx is called on the element are
10708          * run in sequence.  This is the opposite of {@link #syncFx}.
10709          * @return {Roo.Element} The Element
10710          */
10711     sequenceFx : function(){
10712         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10713             block : false,
10714             concurrent : false,
10715             stopFx : false
10716         });
10717         return this;
10718     },
10719
10720         /* @private */
10721     nextFx : function(){
10722         var ef = this.fxQueue[0];
10723         if(ef){
10724             ef.call(this);
10725         }
10726     },
10727
10728         /**
10729          * Returns true if the element has any effects actively running or queued, else returns false.
10730          * @return {Boolean} True if element has active effects, else false
10731          */
10732     hasActiveFx : function(){
10733         return this.fxQueue && this.fxQueue[0];
10734     },
10735
10736         /**
10737          * Stops any running effects and clears the element's internal effects queue if it contains
10738          * any additional effects that haven't started yet.
10739          * @return {Roo.Element} The Element
10740          */
10741     stopFx : function(){
10742         if(this.hasActiveFx()){
10743             var cur = this.fxQueue[0];
10744             if(cur && cur.anim && cur.anim.isAnimated()){
10745                 this.fxQueue = [cur]; // clear out others
10746                 cur.anim.stop(true);
10747             }
10748         }
10749         return this;
10750     },
10751
10752         /* @private */
10753     beforeFx : function(o){
10754         if(this.hasActiveFx() && !o.concurrent){
10755            if(o.stopFx){
10756                this.stopFx();
10757                return true;
10758            }
10759            return false;
10760         }
10761         return true;
10762     },
10763
10764         /**
10765          * Returns true if the element is currently blocking so that no other effect can be queued
10766          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10767          * used to ensure that an effect initiated by a user action runs to completion prior to the
10768          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10769          * @return {Boolean} True if blocking, else false
10770          */
10771     hasFxBlock : function(){
10772         var q = this.fxQueue;
10773         return q && q[0] && q[0].block;
10774     },
10775
10776         /* @private */
10777     queueFx : function(o, fn){
10778         if(!this.fxQueue){
10779             this.fxQueue = [];
10780         }
10781         if(!this.hasFxBlock()){
10782             Roo.applyIf(o, this.fxDefaults);
10783             if(!o.concurrent){
10784                 var run = this.beforeFx(o);
10785                 fn.block = o.block;
10786                 this.fxQueue.push(fn);
10787                 if(run){
10788                     this.nextFx();
10789                 }
10790             }else{
10791                 fn.call(this);
10792             }
10793         }
10794         return this;
10795     },
10796
10797         /* @private */
10798     fxWrap : function(pos, o, vis){
10799         var wrap;
10800         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10801             var wrapXY;
10802             if(o.fixPosition){
10803                 wrapXY = this.getXY();
10804             }
10805             var div = document.createElement("div");
10806             div.style.visibility = vis;
10807             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10808             wrap.setPositioning(pos);
10809             if(wrap.getStyle("position") == "static"){
10810                 wrap.position("relative");
10811             }
10812             this.clearPositioning('auto');
10813             wrap.clip();
10814             wrap.dom.appendChild(this.dom);
10815             if(wrapXY){
10816                 wrap.setXY(wrapXY);
10817             }
10818         }
10819         return wrap;
10820     },
10821
10822         /* @private */
10823     fxUnwrap : function(wrap, pos, o){
10824         this.clearPositioning();
10825         this.setPositioning(pos);
10826         if(!o.wrap){
10827             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10828             wrap.remove();
10829         }
10830     },
10831
10832         /* @private */
10833     getFxRestore : function(){
10834         var st = this.dom.style;
10835         return {pos: this.getPositioning(), width: st.width, height : st.height};
10836     },
10837
10838         /* @private */
10839     afterFx : function(o){
10840         if(o.afterStyle){
10841             this.applyStyles(o.afterStyle);
10842         }
10843         if(o.afterCls){
10844             this.addClass(o.afterCls);
10845         }
10846         if(o.remove === true){
10847             this.remove();
10848         }
10849         Roo.callback(o.callback, o.scope, [this]);
10850         if(!o.concurrent){
10851             this.fxQueue.shift();
10852             this.nextFx();
10853         }
10854     },
10855
10856         /* @private */
10857     getFxEl : function(){ // support for composite element fx
10858         return Roo.get(this.dom);
10859     },
10860
10861         /* @private */
10862     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10863         animType = animType || 'run';
10864         opt = opt || {};
10865         var anim = Roo.lib.Anim[animType](
10866             this.dom, args,
10867             (opt.duration || defaultDur) || .35,
10868             (opt.easing || defaultEase) || 'easeOut',
10869             function(){
10870                 Roo.callback(cb, this);
10871             },
10872             this
10873         );
10874         opt.anim = anim;
10875         return anim;
10876     }
10877 };
10878
10879 // backwords compat
10880 Roo.Fx.resize = Roo.Fx.scale;
10881
10882 //When included, Roo.Fx is automatically applied to Element so that all basic
10883 //effects are available directly via the Element API
10884 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10885  * Based on:
10886  * Ext JS Library 1.1.1
10887  * Copyright(c) 2006-2007, Ext JS, LLC.
10888  *
10889  * Originally Released Under LGPL - original licence link has changed is not relivant.
10890  *
10891  * Fork - LGPL
10892  * <script type="text/javascript">
10893  */
10894
10895
10896 /**
10897  * @class Roo.CompositeElement
10898  * Standard composite class. Creates a Roo.Element for every element in the collection.
10899  * <br><br>
10900  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10901  * actions will be performed on all the elements in this collection.</b>
10902  * <br><br>
10903  * All methods return <i>this</i> and can be chained.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class", true);
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class', true);
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre>
10915  */
10916 Roo.CompositeElement = function(els){
10917     this.elements = [];
10918     this.addElements(els);
10919 };
10920 Roo.CompositeElement.prototype = {
10921     isComposite: true,
10922     addElements : function(els){
10923         if(!els) return this;
10924         if(typeof els == "string"){
10925             els = Roo.Element.selectorFunction(els);
10926         }
10927         var yels = this.elements;
10928         var index = yels.length-1;
10929         for(var i = 0, len = els.length; i < len; i++) {
10930                 yels[++index] = Roo.get(els[i]);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Clears this composite and adds the elements returned by the passed selector.
10937     * @param {String/Array} els A string CSS selector, an array of elements or an element
10938     * @return {CompositeElement} this
10939     */
10940     fill : function(els){
10941         this.elements = [];
10942         this.add(els);
10943         return this;
10944     },
10945
10946     /**
10947     * Filters this composite to only elements that match the passed selector.
10948     * @param {String} selector A string CSS selector
10949     * @param {Boolean} inverse return inverse filter (not matches)
10950     * @return {CompositeElement} this
10951     */
10952     filter : function(selector, inverse){
10953         var els = [];
10954         inverse = inverse || false;
10955         this.each(function(el){
10956             var match = inverse ? !el.is(selector) : el.is(selector);
10957             if(match){
10958                 els[els.length] = el.dom;
10959             }
10960         });
10961         this.fill(els);
10962         return this;
10963     },
10964
10965     invoke : function(fn, args){
10966         var els = this.elements;
10967         for(var i = 0, len = els.length; i < len; i++) {
10968                 Roo.Element.prototype[fn].apply(els[i], args);
10969         }
10970         return this;
10971     },
10972     /**
10973     * Adds elements to this composite.
10974     * @param {String/Array} els A string CSS selector, an array of elements or an element
10975     * @return {CompositeElement} this
10976     */
10977     add : function(els){
10978         if(typeof els == "string"){
10979             this.addElements(Roo.Element.selectorFunction(els));
10980         }else if(els.length !== undefined){
10981             this.addElements(els);
10982         }else{
10983             this.addElements([els]);
10984         }
10985         return this;
10986     },
10987     /**
10988     * Calls the passed function passing (el, this, index) for each element in this composite.
10989     * @param {Function} fn The function to call
10990     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10991     * @return {CompositeElement} this
10992     */
10993     each : function(fn, scope){
10994         var els = this.elements;
10995         for(var i = 0, len = els.length; i < len; i++){
10996             if(fn.call(scope || els[i], els[i], this, i) === false) {
10997                 break;
10998             }
10999         }
11000         return this;
11001     },
11002
11003     /**
11004      * Returns the Element object at the specified index
11005      * @param {Number} index
11006      * @return {Roo.Element}
11007      */
11008     item : function(index){
11009         return this.elements[index] || null;
11010     },
11011
11012     /**
11013      * Returns the first Element
11014      * @return {Roo.Element}
11015      */
11016     first : function(){
11017         return this.item(0);
11018     },
11019
11020     /**
11021      * Returns the last Element
11022      * @return {Roo.Element}
11023      */
11024     last : function(){
11025         return this.item(this.elements.length-1);
11026     },
11027
11028     /**
11029      * Returns the number of elements in this composite
11030      * @return Number
11031      */
11032     getCount : function(){
11033         return this.elements.length;
11034     },
11035
11036     /**
11037      * Returns true if this composite contains the passed element
11038      * @return Boolean
11039      */
11040     contains : function(el){
11041         return this.indexOf(el) !== -1;
11042     },
11043
11044     /**
11045      * Returns true if this composite contains the passed element
11046      * @return Boolean
11047      */
11048     indexOf : function(el){
11049         return this.elements.indexOf(Roo.get(el));
11050     },
11051
11052
11053     /**
11054     * Removes the specified element(s).
11055     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11056     * or an array of any of those.
11057     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11058     * @return {CompositeElement} this
11059     */
11060     removeElement : function(el, removeDom){
11061         if(el instanceof Array){
11062             for(var i = 0, len = el.length; i < len; i++){
11063                 this.removeElement(el[i]);
11064             }
11065             return this;
11066         }
11067         var index = typeof el == 'number' ? el : this.indexOf(el);
11068         if(index !== -1){
11069             if(removeDom){
11070                 var d = this.elements[index];
11071                 if(d.dom){
11072                     d.remove();
11073                 }else{
11074                     d.parentNode.removeChild(d);
11075                 }
11076             }
11077             this.elements.splice(index, 1);
11078         }
11079         return this;
11080     },
11081
11082     /**
11083     * Replaces the specified element with the passed element.
11084     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11085     * to replace.
11086     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11087     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11088     * @return {CompositeElement} this
11089     */
11090     replaceElement : function(el, replacement, domReplace){
11091         var index = typeof el == 'number' ? el : this.indexOf(el);
11092         if(index !== -1){
11093             if(domReplace){
11094                 this.elements[index].replaceWith(replacement);
11095             }else{
11096                 this.elements.splice(index, 1, Roo.get(replacement))
11097             }
11098         }
11099         return this;
11100     },
11101
11102     /**
11103      * Removes all elements.
11104      */
11105     clear : function(){
11106         this.elements = [];
11107     }
11108 };
11109 (function(){
11110     Roo.CompositeElement.createCall = function(proto, fnName){
11111         if(!proto[fnName]){
11112             proto[fnName] = function(){
11113                 return this.invoke(fnName, arguments);
11114             };
11115         }
11116     };
11117     for(var fnName in Roo.Element.prototype){
11118         if(typeof Roo.Element.prototype[fnName] == "function"){
11119             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11120         }
11121     };
11122 })();
11123 /*
11124  * Based on:
11125  * Ext JS Library 1.1.1
11126  * Copyright(c) 2006-2007, Ext JS, LLC.
11127  *
11128  * Originally Released Under LGPL - original licence link has changed is not relivant.
11129  *
11130  * Fork - LGPL
11131  * <script type="text/javascript">
11132  */
11133
11134 /**
11135  * @class Roo.CompositeElementLite
11136  * @extends Roo.CompositeElement
11137  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11138  <pre><code>
11139  var els = Roo.select("#some-el div.some-class");
11140  // or select directly from an existing element
11141  var el = Roo.get('some-el');
11142  el.select('div.some-class');
11143
11144  els.setWidth(100); // all elements become 100 width
11145  els.hide(true); // all elements fade out and hide
11146  // or
11147  els.setWidth(100).hide(true);
11148  </code></pre><br><br>
11149  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11150  * actions will be performed on all the elements in this collection.</b>
11151  */
11152 Roo.CompositeElementLite = function(els){
11153     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11154     this.el = new Roo.Element.Flyweight();
11155 };
11156 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11157     addElements : function(els){
11158         if(els){
11159             if(els instanceof Array){
11160                 this.elements = this.elements.concat(els);
11161             }else{
11162                 var yels = this.elements;
11163                 var index = yels.length-1;
11164                 for(var i = 0, len = els.length; i < len; i++) {
11165                     yels[++index] = els[i];
11166                 }
11167             }
11168         }
11169         return this;
11170     },
11171     invoke : function(fn, args){
11172         var els = this.elements;
11173         var el = this.el;
11174         for(var i = 0, len = els.length; i < len; i++) {
11175             el.dom = els[i];
11176                 Roo.Element.prototype[fn].apply(el, args);
11177         }
11178         return this;
11179     },
11180     /**
11181      * Returns a flyweight Element of the dom element object at the specified index
11182      * @param {Number} index
11183      * @return {Roo.Element}
11184      */
11185     item : function(index){
11186         if(!this.elements[index]){
11187             return null;
11188         }
11189         this.el.dom = this.elements[index];
11190         return this.el;
11191     },
11192
11193     // fixes scope with flyweight
11194     addListener : function(eventName, handler, scope, opt){
11195         var els = this.elements;
11196         for(var i = 0, len = els.length; i < len; i++) {
11197             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11198         }
11199         return this;
11200     },
11201
11202     /**
11203     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11204     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11205     * a reference to the dom node, use el.dom.</b>
11206     * @param {Function} fn The function to call
11207     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11208     * @return {CompositeElement} this
11209     */
11210     each : function(fn, scope){
11211         var els = this.elements;
11212         var el = this.el;
11213         for(var i = 0, len = els.length; i < len; i++){
11214             el.dom = els[i];
11215                 if(fn.call(scope || el, el, this, i) === false){
11216                 break;
11217             }
11218         }
11219         return this;
11220     },
11221
11222     indexOf : function(el){
11223         return this.elements.indexOf(Roo.getDom(el));
11224     },
11225
11226     replaceElement : function(el, replacement, domReplace){
11227         var index = typeof el == 'number' ? el : this.indexOf(el);
11228         if(index !== -1){
11229             replacement = Roo.getDom(replacement);
11230             if(domReplace){
11231                 var d = this.elements[index];
11232                 d.parentNode.insertBefore(replacement, d);
11233                 d.parentNode.removeChild(d);
11234             }
11235             this.elements.splice(index, 1, replacement);
11236         }
11237         return this;
11238     }
11239 });
11240 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11241
11242 /*
11243  * Based on:
11244  * Ext JS Library 1.1.1
11245  * Copyright(c) 2006-2007, Ext JS, LLC.
11246  *
11247  * Originally Released Under LGPL - original licence link has changed is not relivant.
11248  *
11249  * Fork - LGPL
11250  * <script type="text/javascript">
11251  */
11252
11253  
11254
11255 /**
11256  * @class Roo.data.Connection
11257  * @extends Roo.util.Observable
11258  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11259  * either to a configured URL, or to a URL specified at request time.<br><br>
11260  * <p>
11261  * Requests made by this class are asynchronous, and will return immediately. No data from
11262  * the server will be available to the statement immediately following the {@link #request} call.
11263  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11264  * <p>
11265  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11266  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11267  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11268  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11269  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11270  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11271  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11272  * standard DOM methods.
11273  * @constructor
11274  * @param {Object} config a configuration object.
11275  */
11276 Roo.data.Connection = function(config){
11277     Roo.apply(this, config);
11278     this.addEvents({
11279         /**
11280          * @event beforerequest
11281          * Fires before a network request is made to retrieve a data object.
11282          * @param {Connection} conn This Connection object.
11283          * @param {Object} options The options config object passed to the {@link #request} method.
11284          */
11285         "beforerequest" : true,
11286         /**
11287          * @event requestcomplete
11288          * Fires if the request was successfully completed.
11289          * @param {Connection} conn This Connection object.
11290          * @param {Object} response The XHR object containing the response data.
11291          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11292          * @param {Object} options The options config object passed to the {@link #request} method.
11293          */
11294         "requestcomplete" : true,
11295         /**
11296          * @event requestexception
11297          * Fires if an error HTTP status was returned from the server.
11298          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11299          * @param {Connection} conn This Connection object.
11300          * @param {Object} response The XHR object containing the response data.
11301          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11302          * @param {Object} options The options config object passed to the {@link #request} method.
11303          */
11304         "requestexception" : true
11305     });
11306     Roo.data.Connection.superclass.constructor.call(this);
11307 };
11308
11309 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11310     /**
11311      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11312      */
11313     /**
11314      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11315      * extra parameters to each request made by this object. (defaults to undefined)
11316      */
11317     /**
11318      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11319      *  to each request made by this object. (defaults to undefined)
11320      */
11321     /**
11322      * @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)
11323      */
11324     /**
11325      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11326      */
11327     timeout : 30000,
11328     /**
11329      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11330      * @type Boolean
11331      */
11332     autoAbort:false,
11333
11334     /**
11335      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11336      * @type Boolean
11337      */
11338     disableCaching: true,
11339
11340     /**
11341      * Sends an HTTP request to a remote server.
11342      * @param {Object} options An object which may contain the following properties:<ul>
11343      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11344      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11345      * request, a url encoded string or a function to call to get either.</li>
11346      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11347      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11348      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11349      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11350      * <li>options {Object} The parameter to the request call.</li>
11351      * <li>success {Boolean} True if the request succeeded.</li>
11352      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11353      * </ul></li>
11354      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11355      * The callback is passed the following parameters:<ul>
11356      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11357      * <li>options {Object} The parameter to the request call.</li>
11358      * </ul></li>
11359      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11360      * The callback is passed the following parameters:<ul>
11361      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11362      * <li>options {Object} The parameter to the request call.</li>
11363      * </ul></li>
11364      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11365      * for the callback function. Defaults to the browser window.</li>
11366      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11367      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11368      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11369      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11370      * params for the post data. Any params will be appended to the URL.</li>
11371      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11372      * </ul>
11373      * @return {Number} transactionId
11374      */
11375     request : function(o){
11376         if(this.fireEvent("beforerequest", this, o) !== false){
11377             var p = o.params;
11378
11379             if(typeof p == "function"){
11380                 p = p.call(o.scope||window, o);
11381             }
11382             if(typeof p == "object"){
11383                 p = Roo.urlEncode(o.params);
11384             }
11385             if(this.extraParams){
11386                 var extras = Roo.urlEncode(this.extraParams);
11387                 p = p ? (p + '&' + extras) : extras;
11388             }
11389
11390             var url = o.url || this.url;
11391             if(typeof url == 'function'){
11392                 url = url.call(o.scope||window, o);
11393             }
11394
11395             if(o.form){
11396                 var form = Roo.getDom(o.form);
11397                 url = url || form.action;
11398
11399                 var enctype = form.getAttribute("enctype");
11400                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11401                     return this.doFormUpload(o, p, url);
11402                 }
11403                 var f = Roo.lib.Ajax.serializeForm(form);
11404                 p = p ? (p + '&' + f) : f;
11405             }
11406
11407             var hs = o.headers;
11408             if(this.defaultHeaders){
11409                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11410                 if(!o.headers){
11411                     o.headers = hs;
11412                 }
11413             }
11414
11415             var cb = {
11416                 success: this.handleResponse,
11417                 failure: this.handleFailure,
11418                 scope: this,
11419                 argument: {options: o},
11420                 timeout : o.timeout || this.timeout
11421             };
11422
11423             var method = o.method||this.method||(p ? "POST" : "GET");
11424
11425             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11426                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11427             }
11428
11429             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11430                 if(o.autoAbort){
11431                     this.abort();
11432                 }
11433             }else if(this.autoAbort !== false){
11434                 this.abort();
11435             }
11436
11437             if((method == 'GET' && p) || o.xmlData){
11438                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11439                 p = '';
11440             }
11441             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11442             return this.transId;
11443         }else{
11444             Roo.callback(o.callback, o.scope, [o, null, null]);
11445             return null;
11446         }
11447     },
11448
11449     /**
11450      * Determine whether this object has a request outstanding.
11451      * @param {Number} transactionId (Optional) defaults to the last transaction
11452      * @return {Boolean} True if there is an outstanding request.
11453      */
11454     isLoading : function(transId){
11455         if(transId){
11456             return Roo.lib.Ajax.isCallInProgress(transId);
11457         }else{
11458             return this.transId ? true : false;
11459         }
11460     },
11461
11462     /**
11463      * Aborts any outstanding request.
11464      * @param {Number} transactionId (Optional) defaults to the last transaction
11465      */
11466     abort : function(transId){
11467         if(transId || this.isLoading()){
11468             Roo.lib.Ajax.abort(transId || this.transId);
11469         }
11470     },
11471
11472     // private
11473     handleResponse : function(response){
11474         this.transId = false;
11475         var options = response.argument.options;
11476         response.argument = options ? options.argument : null;
11477         this.fireEvent("requestcomplete", this, response, options);
11478         Roo.callback(options.success, options.scope, [response, options]);
11479         Roo.callback(options.callback, options.scope, [options, true, response]);
11480     },
11481
11482     // private
11483     handleFailure : function(response, e){
11484         this.transId = false;
11485         var options = response.argument.options;
11486         response.argument = options ? options.argument : null;
11487         this.fireEvent("requestexception", this, response, options, e);
11488         Roo.callback(options.failure, options.scope, [response, options]);
11489         Roo.callback(options.callback, options.scope, [options, false, response]);
11490     },
11491
11492     // private
11493     doFormUpload : function(o, ps, url){
11494         var id = Roo.id();
11495         var frame = document.createElement('iframe');
11496         frame.id = id;
11497         frame.name = id;
11498         frame.className = 'x-hidden';
11499         if(Roo.isIE){
11500             frame.src = Roo.SSL_SECURE_URL;
11501         }
11502         document.body.appendChild(frame);
11503
11504         if(Roo.isIE){
11505            document.frames[id].name = id;
11506         }
11507
11508         var form = Roo.getDom(o.form);
11509         form.target = id;
11510         form.method = 'POST';
11511         form.enctype = form.encoding = 'multipart/form-data';
11512         if(url){
11513             form.action = url;
11514         }
11515
11516         var hiddens, hd;
11517         if(ps){ // add dynamic params
11518             hiddens = [];
11519             ps = Roo.urlDecode(ps, false);
11520             for(var k in ps){
11521                 if(ps.hasOwnProperty(k)){
11522                     hd = document.createElement('input');
11523                     hd.type = 'hidden';
11524                     hd.name = k;
11525                     hd.value = ps[k];
11526                     form.appendChild(hd);
11527                     hiddens.push(hd);
11528                 }
11529             }
11530         }
11531
11532         function cb(){
11533             var r = {  // bogus response object
11534                 responseText : '',
11535                 responseXML : null
11536             };
11537
11538             r.argument = o ? o.argument : null;
11539
11540             try { //
11541                 var doc;
11542                 if(Roo.isIE){
11543                     doc = frame.contentWindow.document;
11544                 }else {
11545                     doc = (frame.contentDocument || window.frames[id].document);
11546                 }
11547                 if(doc && doc.body){
11548                     r.responseText = doc.body.innerHTML;
11549                 }
11550                 if(doc && doc.XMLDocument){
11551                     r.responseXML = doc.XMLDocument;
11552                 }else {
11553                     r.responseXML = doc;
11554                 }
11555             }
11556             catch(e) {
11557                 // ignore
11558             }
11559
11560             Roo.EventManager.removeListener(frame, 'load', cb, this);
11561
11562             this.fireEvent("requestcomplete", this, r, o);
11563             Roo.callback(o.success, o.scope, [r, o]);
11564             Roo.callback(o.callback, o.scope, [o, true, r]);
11565
11566             setTimeout(function(){document.body.removeChild(frame);}, 100);
11567         }
11568
11569         Roo.EventManager.on(frame, 'load', cb, this);
11570         form.submit();
11571
11572         if(hiddens){ // remove dynamic params
11573             for(var i = 0, len = hiddens.length; i < len; i++){
11574                 form.removeChild(hiddens[i]);
11575             }
11576         }
11577     }
11578 });
11579 /*
11580  * Based on:
11581  * Ext JS Library 1.1.1
11582  * Copyright(c) 2006-2007, Ext JS, LLC.
11583  *
11584  * Originally Released Under LGPL - original licence link has changed is not relivant.
11585  *
11586  * Fork - LGPL
11587  * <script type="text/javascript">
11588  */
11589  
11590 /**
11591  * Global Ajax request class.
11592  * 
11593  * @class Roo.Ajax
11594  * @extends Roo.data.Connection
11595  * @static
11596  * 
11597  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11598  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11599  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11600  * @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)
11601  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11602  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11603  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11604  */
11605 Roo.Ajax = new Roo.data.Connection({
11606     // fix up the docs
11607     /**
11608      * @scope Roo.Ajax
11609      * @type {Boolear} 
11610      */
11611     autoAbort : false,
11612
11613     /**
11614      * Serialize the passed form into a url encoded string
11615      * @scope Roo.Ajax
11616      * @param {String/HTMLElement} form
11617      * @return {String}
11618      */
11619     serializeForm : function(form){
11620         return Roo.lib.Ajax.serializeForm(form);
11621     }
11622 });/*
11623  * Based on:
11624  * Ext JS Library 1.1.1
11625  * Copyright(c) 2006-2007, Ext JS, LLC.
11626  *
11627  * Originally Released Under LGPL - original licence link has changed is not relivant.
11628  *
11629  * Fork - LGPL
11630  * <script type="text/javascript">
11631  */
11632
11633  
11634 /**
11635  * @class Roo.UpdateManager
11636  * @extends Roo.util.Observable
11637  * Provides AJAX-style update for Element object.<br><br>
11638  * Usage:<br>
11639  * <pre><code>
11640  * // Get it from a Roo.Element object
11641  * var el = Roo.get("foo");
11642  * var mgr = el.getUpdateManager();
11643  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11644  * ...
11645  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11646  * <br>
11647  * // or directly (returns the same UpdateManager instance)
11648  * var mgr = new Roo.UpdateManager("myElementId");
11649  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11650  * mgr.on("update", myFcnNeedsToKnow);
11651  * <br>
11652    // short handed call directly from the element object
11653    Roo.get("foo").load({
11654         url: "bar.php",
11655         scripts:true,
11656         params: "for=bar",
11657         text: "Loading Foo..."
11658    });
11659  * </code></pre>
11660  * @constructor
11661  * Create new UpdateManager directly.
11662  * @param {String/HTMLElement/Roo.Element} el The element to update
11663  * @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).
11664  */
11665 Roo.UpdateManager = function(el, forceNew){
11666     el = Roo.get(el);
11667     if(!forceNew && el.updateManager){
11668         return el.updateManager;
11669     }
11670     /**
11671      * The Element object
11672      * @type Roo.Element
11673      */
11674     this.el = el;
11675     /**
11676      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11677      * @type String
11678      */
11679     this.defaultUrl = null;
11680
11681     this.addEvents({
11682         /**
11683          * @event beforeupdate
11684          * Fired before an update is made, return false from your handler and the update is cancelled.
11685          * @param {Roo.Element} el
11686          * @param {String/Object/Function} url
11687          * @param {String/Object} params
11688          */
11689         "beforeupdate": true,
11690         /**
11691          * @event update
11692          * Fired after successful update is made.
11693          * @param {Roo.Element} el
11694          * @param {Object} oResponseObject The response Object
11695          */
11696         "update": true,
11697         /**
11698          * @event failure
11699          * Fired on update failure.
11700          * @param {Roo.Element} el
11701          * @param {Object} oResponseObject The response Object
11702          */
11703         "failure": true
11704     });
11705     var d = Roo.UpdateManager.defaults;
11706     /**
11707      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11708      * @type String
11709      */
11710     this.sslBlankUrl = d.sslBlankUrl;
11711     /**
11712      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11713      * @type Boolean
11714      */
11715     this.disableCaching = d.disableCaching;
11716     /**
11717      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11718      * @type String
11719      */
11720     this.indicatorText = d.indicatorText;
11721     /**
11722      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11723      * @type String
11724      */
11725     this.showLoadIndicator = d.showLoadIndicator;
11726     /**
11727      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11728      * @type Number
11729      */
11730     this.timeout = d.timeout;
11731
11732     /**
11733      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11734      * @type Boolean
11735      */
11736     this.loadScripts = d.loadScripts;
11737
11738     /**
11739      * Transaction object of current executing transaction
11740      */
11741     this.transaction = null;
11742
11743     /**
11744      * @private
11745      */
11746     this.autoRefreshProcId = null;
11747     /**
11748      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11749      * @type Function
11750      */
11751     this.refreshDelegate = this.refresh.createDelegate(this);
11752     /**
11753      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11754      * @type Function
11755      */
11756     this.updateDelegate = this.update.createDelegate(this);
11757     /**
11758      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11759      * @type Function
11760      */
11761     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11762     /**
11763      * @private
11764      */
11765     this.successDelegate = this.processSuccess.createDelegate(this);
11766     /**
11767      * @private
11768      */
11769     this.failureDelegate = this.processFailure.createDelegate(this);
11770
11771     if(!this.renderer){
11772      /**
11773       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11774       */
11775     this.renderer = new Roo.UpdateManager.BasicRenderer();
11776     }
11777     
11778     Roo.UpdateManager.superclass.constructor.call(this);
11779 };
11780
11781 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11782     /**
11783      * Get the Element this UpdateManager is bound to
11784      * @return {Roo.Element} The element
11785      */
11786     getEl : function(){
11787         return this.el;
11788     },
11789     /**
11790      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11791      * @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:
11792 <pre><code>
11793 um.update({<br/>
11794     url: "your-url.php",<br/>
11795     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11796     callback: yourFunction,<br/>
11797     scope: yourObject, //(optional scope)  <br/>
11798     discardUrl: false, <br/>
11799     nocache: false,<br/>
11800     text: "Loading...",<br/>
11801     timeout: 30,<br/>
11802     scripts: false<br/>
11803 });
11804 </code></pre>
11805      * The only required property is url. The optional properties nocache, text and scripts
11806      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11807      * @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}
11808      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11809      * @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.
11810      */
11811     update : function(url, params, callback, discardUrl){
11812         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11813             var method = this.method,
11814                 cfg;
11815             if(typeof url == "object"){ // must be config object
11816                 cfg = url;
11817                 url = cfg.url;
11818                 params = params || cfg.params;
11819                 callback = callback || cfg.callback;
11820                 discardUrl = discardUrl || cfg.discardUrl;
11821                 if(callback && cfg.scope){
11822                     callback = callback.createDelegate(cfg.scope);
11823                 }
11824                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11825                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11826                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11827                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11828                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11829             }
11830             this.showLoading();
11831             if(!discardUrl){
11832                 this.defaultUrl = url;
11833             }
11834             if(typeof url == "function"){
11835                 url = url.call(this);
11836             }
11837
11838             method = method || (params ? "POST" : "GET");
11839             if(method == "GET"){
11840                 url = this.prepareUrl(url);
11841             }
11842
11843             var o = Roo.apply(cfg ||{}, {
11844                 url : url,
11845                 params: params,
11846                 success: this.successDelegate,
11847                 failure: this.failureDelegate,
11848                 callback: undefined,
11849                 timeout: (this.timeout*1000),
11850                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11851             });
11852             Roo.log("updated manager called with timeout of " + o.timeout);
11853             this.transaction = Roo.Ajax.request(o);
11854         }
11855     },
11856
11857     /**
11858      * 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.
11859      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11860      * @param {String/HTMLElement} form The form Id or form element
11861      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11862      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11863      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11864      */
11865     formUpdate : function(form, url, reset, callback){
11866         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11867             if(typeof url == "function"){
11868                 url = url.call(this);
11869             }
11870             form = Roo.getDom(form);
11871             this.transaction = Roo.Ajax.request({
11872                 form: form,
11873                 url:url,
11874                 success: this.successDelegate,
11875                 failure: this.failureDelegate,
11876                 timeout: (this.timeout*1000),
11877                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11878             });
11879             this.showLoading.defer(1, this);
11880         }
11881     },
11882
11883     /**
11884      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11885      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11886      */
11887     refresh : function(callback){
11888         if(this.defaultUrl == null){
11889             return;
11890         }
11891         this.update(this.defaultUrl, null, callback, true);
11892     },
11893
11894     /**
11895      * Set this element to auto refresh.
11896      * @param {Number} interval How often to update (in seconds).
11897      * @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)
11898      * @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}
11899      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11900      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11901      */
11902     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11903         if(refreshNow){
11904             this.update(url || this.defaultUrl, params, callback, true);
11905         }
11906         if(this.autoRefreshProcId){
11907             clearInterval(this.autoRefreshProcId);
11908         }
11909         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11910     },
11911
11912     /**
11913      * Stop auto refresh on this element.
11914      */
11915      stopAutoRefresh : function(){
11916         if(this.autoRefreshProcId){
11917             clearInterval(this.autoRefreshProcId);
11918             delete this.autoRefreshProcId;
11919         }
11920     },
11921
11922     isAutoRefreshing : function(){
11923        return this.autoRefreshProcId ? true : false;
11924     },
11925     /**
11926      * Called to update the element to "Loading" state. Override to perform custom action.
11927      */
11928     showLoading : function(){
11929         if(this.showLoadIndicator){
11930             this.el.update(this.indicatorText);
11931         }
11932     },
11933
11934     /**
11935      * Adds unique parameter to query string if disableCaching = true
11936      * @private
11937      */
11938     prepareUrl : function(url){
11939         if(this.disableCaching){
11940             var append = "_dc=" + (new Date().getTime());
11941             if(url.indexOf("?") !== -1){
11942                 url += "&" + append;
11943             }else{
11944                 url += "?" + append;
11945             }
11946         }
11947         return url;
11948     },
11949
11950     /**
11951      * @private
11952      */
11953     processSuccess : function(response){
11954         this.transaction = null;
11955         if(response.argument.form && response.argument.reset){
11956             try{ // put in try/catch since some older FF releases had problems with this
11957                 response.argument.form.reset();
11958             }catch(e){}
11959         }
11960         if(this.loadScripts){
11961             this.renderer.render(this.el, response, this,
11962                 this.updateComplete.createDelegate(this, [response]));
11963         }else{
11964             this.renderer.render(this.el, response, this);
11965             this.updateComplete(response);
11966         }
11967     },
11968
11969     updateComplete : function(response){
11970         this.fireEvent("update", this.el, response);
11971         if(typeof response.argument.callback == "function"){
11972             response.argument.callback(this.el, true, response);
11973         }
11974     },
11975
11976     /**
11977      * @private
11978      */
11979     processFailure : function(response){
11980         this.transaction = null;
11981         this.fireEvent("failure", this.el, response);
11982         if(typeof response.argument.callback == "function"){
11983             response.argument.callback(this.el, false, response);
11984         }
11985     },
11986
11987     /**
11988      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11989      * @param {Object} renderer The object implementing the render() method
11990      */
11991     setRenderer : function(renderer){
11992         this.renderer = renderer;
11993     },
11994
11995     getRenderer : function(){
11996        return this.renderer;
11997     },
11998
11999     /**
12000      * Set the defaultUrl used for updates
12001      * @param {String/Function} defaultUrl The url or a function to call to get the url
12002      */
12003     setDefaultUrl : function(defaultUrl){
12004         this.defaultUrl = defaultUrl;
12005     },
12006
12007     /**
12008      * Aborts the executing transaction
12009      */
12010     abort : function(){
12011         if(this.transaction){
12012             Roo.Ajax.abort(this.transaction);
12013         }
12014     },
12015
12016     /**
12017      * Returns true if an update is in progress
12018      * @return {Boolean}
12019      */
12020     isUpdating : function(){
12021         if(this.transaction){
12022             return Roo.Ajax.isLoading(this.transaction);
12023         }
12024         return false;
12025     }
12026 });
12027
12028 /**
12029  * @class Roo.UpdateManager.defaults
12030  * @static (not really - but it helps the doc tool)
12031  * The defaults collection enables customizing the default properties of UpdateManager
12032  */
12033    Roo.UpdateManager.defaults = {
12034        /**
12035          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12036          * @type Number
12037          */
12038          timeout : 30,
12039
12040          /**
12041          * True to process scripts by default (Defaults to false).
12042          * @type Boolean
12043          */
12044         loadScripts : false,
12045
12046         /**
12047         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12048         * @type String
12049         */
12050         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12051         /**
12052          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12053          * @type Boolean
12054          */
12055         disableCaching : false,
12056         /**
12057          * Whether to show indicatorText when loading (Defaults to true).
12058          * @type Boolean
12059          */
12060         showLoadIndicator : true,
12061         /**
12062          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12063          * @type String
12064          */
12065         indicatorText : '<div class="loading-indicator">Loading...</div>'
12066    };
12067
12068 /**
12069  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12070  *Usage:
12071  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12072  * @param {String/HTMLElement/Roo.Element} el The element to update
12073  * @param {String} url The url
12074  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12075  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12076  * @static
12077  * @deprecated
12078  * @member Roo.UpdateManager
12079  */
12080 Roo.UpdateManager.updateElement = function(el, url, params, options){
12081     var um = Roo.get(el, true).getUpdateManager();
12082     Roo.apply(um, options);
12083     um.update(url, params, options ? options.callback : null);
12084 };
12085 // alias for backwards compat
12086 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12087 /**
12088  * @class Roo.UpdateManager.BasicRenderer
12089  * Default Content renderer. Updates the elements innerHTML with the responseText.
12090  */
12091 Roo.UpdateManager.BasicRenderer = function(){};
12092
12093 Roo.UpdateManager.BasicRenderer.prototype = {
12094     /**
12095      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12096      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12097      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12098      * @param {Roo.Element} el The element being rendered
12099      * @param {Object} response The YUI Connect response object
12100      * @param {UpdateManager} updateManager The calling update manager
12101      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12102      */
12103      render : function(el, response, updateManager, callback){
12104         el.update(response.responseText, updateManager.loadScripts, callback);
12105     }
12106 };
12107 /*
12108  * Based on:
12109  * Roo JS
12110  * (c)) Alan Knowles
12111  * Licence : LGPL
12112  */
12113
12114
12115 /**
12116  * @class Roo.DomTemplate
12117  * @extends Roo.Template
12118  * An effort at a dom based template engine..
12119  *
12120  * Similar to XTemplate, except it uses dom parsing to create the template..
12121  *
12122  * Supported features:
12123  *
12124  *  Tags:
12125
12126 <pre><code>
12127       {a_variable} - output encoded.
12128       {a_variable.format:("Y-m-d")} - call a method on the variable
12129       {a_variable:raw} - unencoded output
12130       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12131       {a_variable:this.method_on_template(...)} - call a method on the template object.
12132  
12133 </code></pre>
12134  *  The tpl tag:
12135 <pre><code>
12136         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12137         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12138         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12139         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12140   
12141 </code></pre>
12142  *      
12143  */
12144 Roo.DomTemplate = function()
12145 {
12146      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12147      if (this.html) {
12148         this.compile();
12149      }
12150 };
12151
12152
12153 Roo.extend(Roo.DomTemplate, Roo.Template, {
12154     /**
12155      * id counter for sub templates.
12156      */
12157     id : 0,
12158     /**
12159      * flag to indicate if dom parser is inside a pre,
12160      * it will strip whitespace if not.
12161      */
12162     inPre : false,
12163     
12164     /**
12165      * The various sub templates
12166      */
12167     tpls : false,
12168     
12169     
12170     
12171     /**
12172      *
12173      * basic tag replacing syntax
12174      * WORD:WORD()
12175      *
12176      * // you can fake an object call by doing this
12177      *  x.t:(test,tesT) 
12178      * 
12179      */
12180     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12181     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12182     
12183     iterChild : function (node, method) {
12184         
12185         var oldPre = this.inPre;
12186         if (node.tagName == 'PRE') {
12187             this.inPre = true;
12188         }
12189         for( var i = 0; i < node.childNodes.length; i++) {
12190             method.call(this, node.childNodes[i]);
12191         }
12192         this.inPre = oldPre;
12193     },
12194     
12195     
12196     
12197     /**
12198      * compile the template
12199      *
12200      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12201      *
12202      */
12203     compile: function()
12204     {
12205         var s = this.html;
12206         
12207         // covert the html into DOM...
12208         var doc = false;
12209         var div =false;
12210         try {
12211             doc = document.implementation.createHTMLDocument("");
12212             doc.documentElement.innerHTML =   this.html  ;
12213             div = doc.documentElement;
12214         } catch (e) {
12215             // old IE... - nasty -- it causes all sorts of issues.. with
12216             // images getting pulled from server..
12217             div = document.createElement('div');
12218             div.innerHTML = this.html;
12219         }
12220         //doc.documentElement.innerHTML = htmlBody
12221          
12222         
12223         
12224         this.tpls = [];
12225         var _t = this;
12226         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12227         
12228         var tpls = this.tpls;
12229         
12230         // create a top level template from the snippet..
12231         
12232         //Roo.log(div.innerHTML);
12233         
12234         var tpl = {
12235             uid : 'master',
12236             id : this.id++,
12237             attr : false,
12238             value : false,
12239             body : div.innerHTML,
12240             
12241             forCall : false,
12242             execCall : false,
12243             dom : div,
12244             isTop : true
12245             
12246         };
12247         tpls.unshift(tpl);
12248         
12249         
12250         // compile them...
12251         this.tpls = [];
12252         Roo.each(tpls, function(tp){
12253             this.compileTpl(tp);
12254             this.tpls[tp.id] = tp;
12255         }, this);
12256         
12257         this.master = tpls[0];
12258         return this;
12259         
12260         
12261     },
12262     
12263     compileNode : function(node, istop) {
12264         // test for
12265         //Roo.log(node);
12266         
12267         
12268         // skip anything not a tag..
12269         if (node.nodeType != 1) {
12270             if (node.nodeType == 3 && !this.inPre) {
12271                 // reduce white space..
12272                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12273                 
12274             }
12275             return;
12276         }
12277         
12278         var tpl = {
12279             uid : false,
12280             id : false,
12281             attr : false,
12282             value : false,
12283             body : '',
12284             
12285             forCall : false,
12286             execCall : false,
12287             dom : false,
12288             isTop : istop
12289             
12290             
12291         };
12292         
12293         
12294         switch(true) {
12295             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12296             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12297             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12298             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12299             // no default..
12300         }
12301         
12302         
12303         if (!tpl.attr) {
12304             // just itterate children..
12305             this.iterChild(node,this.compileNode);
12306             return;
12307         }
12308         tpl.uid = this.id++;
12309         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12310         node.removeAttribute('roo-'+ tpl.attr);
12311         if (tpl.attr != 'name') {
12312             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12313             node.parentNode.replaceChild(placeholder,  node);
12314         } else {
12315             
12316             var placeholder =  document.createElement('span');
12317             placeholder.className = 'roo-tpl-' + tpl.value;
12318             node.parentNode.replaceChild(placeholder,  node);
12319         }
12320         
12321         // parent now sees '{domtplXXXX}
12322         this.iterChild(node,this.compileNode);
12323         
12324         // we should now have node body...
12325         var div = document.createElement('div');
12326         div.appendChild(node);
12327         tpl.dom = node;
12328         // this has the unfortunate side effect of converting tagged attributes
12329         // eg. href="{...}" into %7C...%7D
12330         // this has been fixed by searching for those combo's although it's a bit hacky..
12331         
12332         
12333         tpl.body = div.innerHTML;
12334         
12335         
12336          
12337         tpl.id = tpl.uid;
12338         switch(tpl.attr) {
12339             case 'for' :
12340                 switch (tpl.value) {
12341                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12342                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12343                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12344                 }
12345                 break;
12346             
12347             case 'exec':
12348                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12349                 break;
12350             
12351             case 'if':     
12352                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12353                 break;
12354             
12355             case 'name':
12356                 tpl.id  = tpl.value; // replace non characters???
12357                 break;
12358             
12359         }
12360         
12361         
12362         this.tpls.push(tpl);
12363         
12364         
12365         
12366     },
12367     
12368     
12369     
12370     
12371     /**
12372      * Compile a segment of the template into a 'sub-template'
12373      *
12374      * 
12375      * 
12376      *
12377      */
12378     compileTpl : function(tpl)
12379     {
12380         var fm = Roo.util.Format;
12381         var useF = this.disableFormats !== true;
12382         
12383         var sep = Roo.isGecko ? "+\n" : ",\n";
12384         
12385         var undef = function(str) {
12386             Roo.debug && Roo.log("Property not found :"  + str);
12387             return '';
12388         };
12389           
12390         //Roo.log(tpl.body);
12391         
12392         
12393         
12394         var fn = function(m, lbrace, name, format, args)
12395         {
12396             //Roo.log("ARGS");
12397             //Roo.log(arguments);
12398             args = args ? args.replace(/\\'/g,"'") : args;
12399             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12400             if (typeof(format) == 'undefined') {
12401                 format =  'htmlEncode'; 
12402             }
12403             if (format == 'raw' ) {
12404                 format = false;
12405             }
12406             
12407             if(name.substr(0, 6) == 'domtpl'){
12408                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12409             }
12410             
12411             // build an array of options to determine if value is undefined..
12412             
12413             // basically get 'xxxx.yyyy' then do
12414             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12415             //    (function () { Roo.log("Property not found"); return ''; })() :
12416             //    ......
12417             
12418             var udef_ar = [];
12419             var lookfor = '';
12420             Roo.each(name.split('.'), function(st) {
12421                 lookfor += (lookfor.length ? '.': '') + st;
12422                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12423             });
12424             
12425             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12426             
12427             
12428             if(format && useF){
12429                 
12430                 args = args ? ',' + args : "";
12431                  
12432                 if(format.substr(0, 5) != "this."){
12433                     format = "fm." + format + '(';
12434                 }else{
12435                     format = 'this.call("'+ format.substr(5) + '", ';
12436                     args = ", values";
12437                 }
12438                 
12439                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12440             }
12441              
12442             if (args && args.length) {
12443                 // called with xxyx.yuu:(test,test)
12444                 // change to ()
12445                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12446             }
12447             // raw.. - :raw modifier..
12448             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12449             
12450         };
12451         var body;
12452         // branched to use + in gecko and [].join() in others
12453         if(Roo.isGecko){
12454             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12455                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12456                     "';};};";
12457         }else{
12458             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12459             body.push(tpl.body.replace(/(\r\n|\n)/g,
12460                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12461             body.push("'].join('');};};");
12462             body = body.join('');
12463         }
12464         
12465         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12466        
12467         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12468         eval(body);
12469         
12470         return this;
12471     },
12472      
12473     /**
12474      * same as applyTemplate, except it's done to one of the subTemplates
12475      * when using named templates, you can do:
12476      *
12477      * var str = pl.applySubTemplate('your-name', values);
12478      *
12479      * 
12480      * @param {Number} id of the template
12481      * @param {Object} values to apply to template
12482      * @param {Object} parent (normaly the instance of this object)
12483      */
12484     applySubTemplate : function(id, values, parent)
12485     {
12486         
12487         
12488         var t = this.tpls[id];
12489         
12490         
12491         try { 
12492             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12493                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12494                 return '';
12495             }
12496         } catch(e) {
12497             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12498             Roo.log(values);
12499           
12500             return '';
12501         }
12502         try { 
12503             
12504             if(t.execCall && t.execCall.call(this, values, parent)){
12505                 return '';
12506             }
12507         } catch(e) {
12508             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12509             Roo.log(values);
12510             return '';
12511         }
12512         
12513         try {
12514             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12515             parent = t.target ? values : parent;
12516             if(t.forCall && vs instanceof Array){
12517                 var buf = [];
12518                 for(var i = 0, len = vs.length; i < len; i++){
12519                     try {
12520                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12521                     } catch (e) {
12522                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12523                         Roo.log(e.body);
12524                         //Roo.log(t.compiled);
12525                         Roo.log(vs[i]);
12526                     }   
12527                 }
12528                 return buf.join('');
12529             }
12530         } catch (e) {
12531             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12532             Roo.log(values);
12533             return '';
12534         }
12535         try {
12536             return t.compiled.call(this, vs, parent);
12537         } catch (e) {
12538             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12539             Roo.log(e.body);
12540             //Roo.log(t.compiled);
12541             Roo.log(values);
12542             return '';
12543         }
12544     },
12545
12546    
12547
12548     applyTemplate : function(values){
12549         return this.master.compiled.call(this, values, {});
12550         //var s = this.subs;
12551     },
12552
12553     apply : function(){
12554         return this.applyTemplate.apply(this, arguments);
12555     }
12556
12557  });
12558
12559 Roo.DomTemplate.from = function(el){
12560     el = Roo.getDom(el);
12561     return new Roo.Domtemplate(el.value || el.innerHTML);
12562 };/*
12563  * Based on:
12564  * Ext JS Library 1.1.1
12565  * Copyright(c) 2006-2007, Ext JS, LLC.
12566  *
12567  * Originally Released Under LGPL - original licence link has changed is not relivant.
12568  *
12569  * Fork - LGPL
12570  * <script type="text/javascript">
12571  */
12572
12573 /**
12574  * @class Roo.util.DelayedTask
12575  * Provides a convenient method of performing setTimeout where a new
12576  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12577  * You can use this class to buffer
12578  * the keypress events for a certain number of milliseconds, and perform only if they stop
12579  * for that amount of time.
12580  * @constructor The parameters to this constructor serve as defaults and are not required.
12581  * @param {Function} fn (optional) The default function to timeout
12582  * @param {Object} scope (optional) The default scope of that timeout
12583  * @param {Array} args (optional) The default Array of arguments
12584  */
12585 Roo.util.DelayedTask = function(fn, scope, args){
12586     var id = null, d, t;
12587
12588     var call = function(){
12589         var now = new Date().getTime();
12590         if(now - t >= d){
12591             clearInterval(id);
12592             id = null;
12593             fn.apply(scope, args || []);
12594         }
12595     };
12596     /**
12597      * Cancels any pending timeout and queues a new one
12598      * @param {Number} delay The milliseconds to delay
12599      * @param {Function} newFn (optional) Overrides function passed to constructor
12600      * @param {Object} newScope (optional) Overrides scope passed to constructor
12601      * @param {Array} newArgs (optional) Overrides args passed to constructor
12602      */
12603     this.delay = function(delay, newFn, newScope, newArgs){
12604         if(id && delay != d){
12605             this.cancel();
12606         }
12607         d = delay;
12608         t = new Date().getTime();
12609         fn = newFn || fn;
12610         scope = newScope || scope;
12611         args = newArgs || args;
12612         if(!id){
12613             id = setInterval(call, d);
12614         }
12615     };
12616
12617     /**
12618      * Cancel the last queued timeout
12619      */
12620     this.cancel = function(){
12621         if(id){
12622             clearInterval(id);
12623             id = null;
12624         }
12625     };
12626 };/*
12627  * Based on:
12628  * Ext JS Library 1.1.1
12629  * Copyright(c) 2006-2007, Ext JS, LLC.
12630  *
12631  * Originally Released Under LGPL - original licence link has changed is not relivant.
12632  *
12633  * Fork - LGPL
12634  * <script type="text/javascript">
12635  */
12636  
12637  
12638 Roo.util.TaskRunner = function(interval){
12639     interval = interval || 10;
12640     var tasks = [], removeQueue = [];
12641     var id = 0;
12642     var running = false;
12643
12644     var stopThread = function(){
12645         running = false;
12646         clearInterval(id);
12647         id = 0;
12648     };
12649
12650     var startThread = function(){
12651         if(!running){
12652             running = true;
12653             id = setInterval(runTasks, interval);
12654         }
12655     };
12656
12657     var removeTask = function(task){
12658         removeQueue.push(task);
12659         if(task.onStop){
12660             task.onStop();
12661         }
12662     };
12663
12664     var runTasks = function(){
12665         if(removeQueue.length > 0){
12666             for(var i = 0, len = removeQueue.length; i < len; i++){
12667                 tasks.remove(removeQueue[i]);
12668             }
12669             removeQueue = [];
12670             if(tasks.length < 1){
12671                 stopThread();
12672                 return;
12673             }
12674         }
12675         var now = new Date().getTime();
12676         for(var i = 0, len = tasks.length; i < len; ++i){
12677             var t = tasks[i];
12678             var itime = now - t.taskRunTime;
12679             if(t.interval <= itime){
12680                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12681                 t.taskRunTime = now;
12682                 if(rt === false || t.taskRunCount === t.repeat){
12683                     removeTask(t);
12684                     return;
12685                 }
12686             }
12687             if(t.duration && t.duration <= (now - t.taskStartTime)){
12688                 removeTask(t);
12689             }
12690         }
12691     };
12692
12693     /**
12694      * Queues a new task.
12695      * @param {Object} task
12696      */
12697     this.start = function(task){
12698         tasks.push(task);
12699         task.taskStartTime = new Date().getTime();
12700         task.taskRunTime = 0;
12701         task.taskRunCount = 0;
12702         startThread();
12703         return task;
12704     };
12705
12706     this.stop = function(task){
12707         removeTask(task);
12708         return task;
12709     };
12710
12711     this.stopAll = function(){
12712         stopThread();
12713         for(var i = 0, len = tasks.length; i < len; i++){
12714             if(tasks[i].onStop){
12715                 tasks[i].onStop();
12716             }
12717         }
12718         tasks = [];
12719         removeQueue = [];
12720     };
12721 };
12722
12723 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12724  * Based on:
12725  * Ext JS Library 1.1.1
12726  * Copyright(c) 2006-2007, Ext JS, LLC.
12727  *
12728  * Originally Released Under LGPL - original licence link has changed is not relivant.
12729  *
12730  * Fork - LGPL
12731  * <script type="text/javascript">
12732  */
12733
12734  
12735 /**
12736  * @class Roo.util.MixedCollection
12737  * @extends Roo.util.Observable
12738  * A Collection class that maintains both numeric indexes and keys and exposes events.
12739  * @constructor
12740  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12741  * collection (defaults to false)
12742  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12743  * and return the key value for that item.  This is used when available to look up the key on items that
12744  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12745  * equivalent to providing an implementation for the {@link #getKey} method.
12746  */
12747 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12748     this.items = [];
12749     this.map = {};
12750     this.keys = [];
12751     this.length = 0;
12752     this.addEvents({
12753         /**
12754          * @event clear
12755          * Fires when the collection is cleared.
12756          */
12757         "clear" : true,
12758         /**
12759          * @event add
12760          * Fires when an item is added to the collection.
12761          * @param {Number} index The index at which the item was added.
12762          * @param {Object} o The item added.
12763          * @param {String} key The key associated with the added item.
12764          */
12765         "add" : true,
12766         /**
12767          * @event replace
12768          * Fires when an item is replaced in the collection.
12769          * @param {String} key he key associated with the new added.
12770          * @param {Object} old The item being replaced.
12771          * @param {Object} new The new item.
12772          */
12773         "replace" : true,
12774         /**
12775          * @event remove
12776          * Fires when an item is removed from the collection.
12777          * @param {Object} o The item being removed.
12778          * @param {String} key (optional) The key associated with the removed item.
12779          */
12780         "remove" : true,
12781         "sort" : true
12782     });
12783     this.allowFunctions = allowFunctions === true;
12784     if(keyFn){
12785         this.getKey = keyFn;
12786     }
12787     Roo.util.MixedCollection.superclass.constructor.call(this);
12788 };
12789
12790 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12791     allowFunctions : false,
12792     
12793 /**
12794  * Adds an item to the collection.
12795  * @param {String} key The key to associate with the item
12796  * @param {Object} o The item to add.
12797  * @return {Object} The item added.
12798  */
12799     add : function(key, o){
12800         if(arguments.length == 1){
12801             o = arguments[0];
12802             key = this.getKey(o);
12803         }
12804         if(typeof key == "undefined" || key === null){
12805             this.length++;
12806             this.items.push(o);
12807             this.keys.push(null);
12808         }else{
12809             var old = this.map[key];
12810             if(old){
12811                 return this.replace(key, o);
12812             }
12813             this.length++;
12814             this.items.push(o);
12815             this.map[key] = o;
12816             this.keys.push(key);
12817         }
12818         this.fireEvent("add", this.length-1, o, key);
12819         return o;
12820     },
12821        
12822 /**
12823   * MixedCollection has a generic way to fetch keys if you implement getKey.
12824 <pre><code>
12825 // normal way
12826 var mc = new Roo.util.MixedCollection();
12827 mc.add(someEl.dom.id, someEl);
12828 mc.add(otherEl.dom.id, otherEl);
12829 //and so on
12830
12831 // using getKey
12832 var mc = new Roo.util.MixedCollection();
12833 mc.getKey = function(el){
12834    return el.dom.id;
12835 };
12836 mc.add(someEl);
12837 mc.add(otherEl);
12838
12839 // or via the constructor
12840 var mc = new Roo.util.MixedCollection(false, function(el){
12841    return el.dom.id;
12842 });
12843 mc.add(someEl);
12844 mc.add(otherEl);
12845 </code></pre>
12846  * @param o {Object} The item for which to find the key.
12847  * @return {Object} The key for the passed item.
12848  */
12849     getKey : function(o){
12850          return o.id; 
12851     },
12852    
12853 /**
12854  * Replaces an item in the collection.
12855  * @param {String} key The key associated with the item to replace, or the item to replace.
12856  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12857  * @return {Object}  The new item.
12858  */
12859     replace : function(key, o){
12860         if(arguments.length == 1){
12861             o = arguments[0];
12862             key = this.getKey(o);
12863         }
12864         var old = this.item(key);
12865         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12866              return this.add(key, o);
12867         }
12868         var index = this.indexOfKey(key);
12869         this.items[index] = o;
12870         this.map[key] = o;
12871         this.fireEvent("replace", key, old, o);
12872         return o;
12873     },
12874    
12875 /**
12876  * Adds all elements of an Array or an Object to the collection.
12877  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12878  * an Array of values, each of which are added to the collection.
12879  */
12880     addAll : function(objs){
12881         if(arguments.length > 1 || objs instanceof Array){
12882             var args = arguments.length > 1 ? arguments : objs;
12883             for(var i = 0, len = args.length; i < len; i++){
12884                 this.add(args[i]);
12885             }
12886         }else{
12887             for(var key in objs){
12888                 if(this.allowFunctions || typeof objs[key] != "function"){
12889                     this.add(key, objs[key]);
12890                 }
12891             }
12892         }
12893     },
12894    
12895 /**
12896  * Executes the specified function once for every item in the collection, passing each
12897  * item as the first and only parameter. returning false from the function will stop the iteration.
12898  * @param {Function} fn The function to execute for each item.
12899  * @param {Object} scope (optional) The scope in which to execute the function.
12900  */
12901     each : function(fn, scope){
12902         var items = [].concat(this.items); // each safe for removal
12903         for(var i = 0, len = items.length; i < len; i++){
12904             if(fn.call(scope || items[i], items[i], i, len) === false){
12905                 break;
12906             }
12907         }
12908     },
12909    
12910 /**
12911  * Executes the specified function once for every key in the collection, passing each
12912  * key, and its associated item as the first two parameters.
12913  * @param {Function} fn The function to execute for each item.
12914  * @param {Object} scope (optional) The scope in which to execute the function.
12915  */
12916     eachKey : function(fn, scope){
12917         for(var i = 0, len = this.keys.length; i < len; i++){
12918             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12919         }
12920     },
12921    
12922 /**
12923  * Returns the first item in the collection which elicits a true return value from the
12924  * passed selection function.
12925  * @param {Function} fn The selection function to execute for each item.
12926  * @param {Object} scope (optional) The scope in which to execute the function.
12927  * @return {Object} The first item in the collection which returned true from the selection function.
12928  */
12929     find : function(fn, scope){
12930         for(var i = 0, len = this.items.length; i < len; i++){
12931             if(fn.call(scope || window, this.items[i], this.keys[i])){
12932                 return this.items[i];
12933             }
12934         }
12935         return null;
12936     },
12937    
12938 /**
12939  * Inserts an item at the specified index in the collection.
12940  * @param {Number} index The index to insert the item at.
12941  * @param {String} key The key to associate with the new item, or the item itself.
12942  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12943  * @return {Object} The item inserted.
12944  */
12945     insert : function(index, key, o){
12946         if(arguments.length == 2){
12947             o = arguments[1];
12948             key = this.getKey(o);
12949         }
12950         if(index >= this.length){
12951             return this.add(key, o);
12952         }
12953         this.length++;
12954         this.items.splice(index, 0, o);
12955         if(typeof key != "undefined" && key != null){
12956             this.map[key] = o;
12957         }
12958         this.keys.splice(index, 0, key);
12959         this.fireEvent("add", index, o, key);
12960         return o;
12961     },
12962    
12963 /**
12964  * Removed an item from the collection.
12965  * @param {Object} o The item to remove.
12966  * @return {Object} The item removed.
12967  */
12968     remove : function(o){
12969         return this.removeAt(this.indexOf(o));
12970     },
12971    
12972 /**
12973  * Remove an item from a specified index in the collection.
12974  * @param {Number} index The index within the collection of the item to remove.
12975  */
12976     removeAt : function(index){
12977         if(index < this.length && index >= 0){
12978             this.length--;
12979             var o = this.items[index];
12980             this.items.splice(index, 1);
12981             var key = this.keys[index];
12982             if(typeof key != "undefined"){
12983                 delete this.map[key];
12984             }
12985             this.keys.splice(index, 1);
12986             this.fireEvent("remove", o, key);
12987         }
12988     },
12989    
12990 /**
12991  * Removed an item associated with the passed key fom the collection.
12992  * @param {String} key The key of the item to remove.
12993  */
12994     removeKey : function(key){
12995         return this.removeAt(this.indexOfKey(key));
12996     },
12997    
12998 /**
12999  * Returns the number of items in the collection.
13000  * @return {Number} the number of items in the collection.
13001  */
13002     getCount : function(){
13003         return this.length; 
13004     },
13005    
13006 /**
13007  * Returns index within the collection of the passed Object.
13008  * @param {Object} o The item to find the index of.
13009  * @return {Number} index of the item.
13010  */
13011     indexOf : function(o){
13012         if(!this.items.indexOf){
13013             for(var i = 0, len = this.items.length; i < len; i++){
13014                 if(this.items[i] == o) return i;
13015             }
13016             return -1;
13017         }else{
13018             return this.items.indexOf(o);
13019         }
13020     },
13021    
13022 /**
13023  * Returns index within the collection of the passed key.
13024  * @param {String} key The key to find the index of.
13025  * @return {Number} index of the key.
13026  */
13027     indexOfKey : function(key){
13028         if(!this.keys.indexOf){
13029             for(var i = 0, len = this.keys.length; i < len; i++){
13030                 if(this.keys[i] == key) return i;
13031             }
13032             return -1;
13033         }else{
13034             return this.keys.indexOf(key);
13035         }
13036     },
13037    
13038 /**
13039  * Returns the item associated with the passed key OR index. Key has priority over index.
13040  * @param {String/Number} key The key or index of the item.
13041  * @return {Object} The item associated with the passed key.
13042  */
13043     item : function(key){
13044         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13045         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13046     },
13047     
13048 /**
13049  * Returns the item at the specified index.
13050  * @param {Number} index The index of the item.
13051  * @return {Object}
13052  */
13053     itemAt : function(index){
13054         return this.items[index];
13055     },
13056     
13057 /**
13058  * Returns the item associated with the passed key.
13059  * @param {String/Number} key The key of the item.
13060  * @return {Object} The item associated with the passed key.
13061  */
13062     key : function(key){
13063         return this.map[key];
13064     },
13065    
13066 /**
13067  * Returns true if the collection contains the passed Object as an item.
13068  * @param {Object} o  The Object to look for in the collection.
13069  * @return {Boolean} True if the collection contains the Object as an item.
13070  */
13071     contains : function(o){
13072         return this.indexOf(o) != -1;
13073     },
13074    
13075 /**
13076  * Returns true if the collection contains the passed Object as a key.
13077  * @param {String} key The key to look for in the collection.
13078  * @return {Boolean} True if the collection contains the Object as a key.
13079  */
13080     containsKey : function(key){
13081         return typeof this.map[key] != "undefined";
13082     },
13083    
13084 /**
13085  * Removes all items from the collection.
13086  */
13087     clear : function(){
13088         this.length = 0;
13089         this.items = [];
13090         this.keys = [];
13091         this.map = {};
13092         this.fireEvent("clear");
13093     },
13094    
13095 /**
13096  * Returns the first item in the collection.
13097  * @return {Object} the first item in the collection..
13098  */
13099     first : function(){
13100         return this.items[0]; 
13101     },
13102    
13103 /**
13104  * Returns the last item in the collection.
13105  * @return {Object} the last item in the collection..
13106  */
13107     last : function(){
13108         return this.items[this.length-1];   
13109     },
13110     
13111     _sort : function(property, dir, fn){
13112         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13113         fn = fn || function(a, b){
13114             return a-b;
13115         };
13116         var c = [], k = this.keys, items = this.items;
13117         for(var i = 0, len = items.length; i < len; i++){
13118             c[c.length] = {key: k[i], value: items[i], index: i};
13119         }
13120         c.sort(function(a, b){
13121             var v = fn(a[property], b[property]) * dsc;
13122             if(v == 0){
13123                 v = (a.index < b.index ? -1 : 1);
13124             }
13125             return v;
13126         });
13127         for(var i = 0, len = c.length; i < len; i++){
13128             items[i] = c[i].value;
13129             k[i] = c[i].key;
13130         }
13131         this.fireEvent("sort", this);
13132     },
13133     
13134     /**
13135      * Sorts this collection with the passed comparison function
13136      * @param {String} direction (optional) "ASC" or "DESC"
13137      * @param {Function} fn (optional) comparison function
13138      */
13139     sort : function(dir, fn){
13140         this._sort("value", dir, fn);
13141     },
13142     
13143     /**
13144      * Sorts this collection by keys
13145      * @param {String} direction (optional) "ASC" or "DESC"
13146      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13147      */
13148     keySort : function(dir, fn){
13149         this._sort("key", dir, fn || function(a, b){
13150             return String(a).toUpperCase()-String(b).toUpperCase();
13151         });
13152     },
13153     
13154     /**
13155      * Returns a range of items in this collection
13156      * @param {Number} startIndex (optional) defaults to 0
13157      * @param {Number} endIndex (optional) default to the last item
13158      * @return {Array} An array of items
13159      */
13160     getRange : function(start, end){
13161         var items = this.items;
13162         if(items.length < 1){
13163             return [];
13164         }
13165         start = start || 0;
13166         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13167         var r = [];
13168         if(start <= end){
13169             for(var i = start; i <= end; i++) {
13170                     r[r.length] = items[i];
13171             }
13172         }else{
13173             for(var i = start; i >= end; i--) {
13174                     r[r.length] = items[i];
13175             }
13176         }
13177         return r;
13178     },
13179         
13180     /**
13181      * Filter the <i>objects</i> in this collection by a specific property. 
13182      * Returns a new collection that has been filtered.
13183      * @param {String} property A property on your objects
13184      * @param {String/RegExp} value Either string that the property values 
13185      * should start with or a RegExp to test against the property
13186      * @return {MixedCollection} The new filtered collection
13187      */
13188     filter : function(property, value){
13189         if(!value.exec){ // not a regex
13190             value = String(value);
13191             if(value.length == 0){
13192                 return this.clone();
13193             }
13194             value = new RegExp("^" + Roo.escapeRe(value), "i");
13195         }
13196         return this.filterBy(function(o){
13197             return o && value.test(o[property]);
13198         });
13199         },
13200     
13201     /**
13202      * Filter by a function. * Returns a new collection that has been filtered.
13203      * The passed function will be called with each 
13204      * object in the collection. If the function returns true, the value is included 
13205      * otherwise it is filtered.
13206      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13207      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13208      * @return {MixedCollection} The new filtered collection
13209      */
13210     filterBy : function(fn, scope){
13211         var r = new Roo.util.MixedCollection();
13212         r.getKey = this.getKey;
13213         var k = this.keys, it = this.items;
13214         for(var i = 0, len = it.length; i < len; i++){
13215             if(fn.call(scope||this, it[i], k[i])){
13216                                 r.add(k[i], it[i]);
13217                         }
13218         }
13219         return r;
13220     },
13221     
13222     /**
13223      * Creates a duplicate of this collection
13224      * @return {MixedCollection}
13225      */
13226     clone : function(){
13227         var r = new Roo.util.MixedCollection();
13228         var k = this.keys, it = this.items;
13229         for(var i = 0, len = it.length; i < len; i++){
13230             r.add(k[i], it[i]);
13231         }
13232         r.getKey = this.getKey;
13233         return r;
13234     }
13235 });
13236 /**
13237  * Returns the item associated with the passed key or index.
13238  * @method
13239  * @param {String/Number} key The key or index of the item.
13240  * @return {Object} The item associated with the passed key.
13241  */
13242 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13243  * Based on:
13244  * Ext JS Library 1.1.1
13245  * Copyright(c) 2006-2007, Ext JS, LLC.
13246  *
13247  * Originally Released Under LGPL - original licence link has changed is not relivant.
13248  *
13249  * Fork - LGPL
13250  * <script type="text/javascript">
13251  */
13252 /**
13253  * @class Roo.util.JSON
13254  * Modified version of Douglas Crockford"s json.js that doesn"t
13255  * mess with the Object prototype 
13256  * http://www.json.org/js.html
13257  * @singleton
13258  */
13259 Roo.util.JSON = new (function(){
13260     var useHasOwn = {}.hasOwnProperty ? true : false;
13261     
13262     // crashes Safari in some instances
13263     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13264     
13265     var pad = function(n) {
13266         return n < 10 ? "0" + n : n;
13267     };
13268     
13269     var m = {
13270         "\b": '\\b',
13271         "\t": '\\t',
13272         "\n": '\\n',
13273         "\f": '\\f',
13274         "\r": '\\r',
13275         '"' : '\\"',
13276         "\\": '\\\\'
13277     };
13278
13279     var encodeString = function(s){
13280         if (/["\\\x00-\x1f]/.test(s)) {
13281             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13282                 var c = m[b];
13283                 if(c){
13284                     return c;
13285                 }
13286                 c = b.charCodeAt();
13287                 return "\\u00" +
13288                     Math.floor(c / 16).toString(16) +
13289                     (c % 16).toString(16);
13290             }) + '"';
13291         }
13292         return '"' + s + '"';
13293     };
13294     
13295     var encodeArray = function(o){
13296         var a = ["["], b, i, l = o.length, v;
13297             for (i = 0; i < l; i += 1) {
13298                 v = o[i];
13299                 switch (typeof v) {
13300                     case "undefined":
13301                     case "function":
13302                     case "unknown":
13303                         break;
13304                     default:
13305                         if (b) {
13306                             a.push(',');
13307                         }
13308                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13309                         b = true;
13310                 }
13311             }
13312             a.push("]");
13313             return a.join("");
13314     };
13315     
13316     var encodeDate = function(o){
13317         return '"' + o.getFullYear() + "-" +
13318                 pad(o.getMonth() + 1) + "-" +
13319                 pad(o.getDate()) + "T" +
13320                 pad(o.getHours()) + ":" +
13321                 pad(o.getMinutes()) + ":" +
13322                 pad(o.getSeconds()) + '"';
13323     };
13324     
13325     /**
13326      * Encodes an Object, Array or other value
13327      * @param {Mixed} o The variable to encode
13328      * @return {String} The JSON string
13329      */
13330     this.encode = function(o)
13331     {
13332         // should this be extended to fully wrap stringify..
13333         
13334         if(typeof o == "undefined" || o === null){
13335             return "null";
13336         }else if(o instanceof Array){
13337             return encodeArray(o);
13338         }else if(o instanceof Date){
13339             return encodeDate(o);
13340         }else if(typeof o == "string"){
13341             return encodeString(o);
13342         }else if(typeof o == "number"){
13343             return isFinite(o) ? String(o) : "null";
13344         }else if(typeof o == "boolean"){
13345             return String(o);
13346         }else {
13347             var a = ["{"], b, i, v;
13348             for (i in o) {
13349                 if(!useHasOwn || o.hasOwnProperty(i)) {
13350                     v = o[i];
13351                     switch (typeof v) {
13352                     case "undefined":
13353                     case "function":
13354                     case "unknown":
13355                         break;
13356                     default:
13357                         if(b){
13358                             a.push(',');
13359                         }
13360                         a.push(this.encode(i), ":",
13361                                 v === null ? "null" : this.encode(v));
13362                         b = true;
13363                     }
13364                 }
13365             }
13366             a.push("}");
13367             return a.join("");
13368         }
13369     };
13370     
13371     /**
13372      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13373      * @param {String} json The JSON string
13374      * @return {Object} The resulting object
13375      */
13376     this.decode = function(json){
13377         
13378         return  /** eval:var:json */ eval("(" + json + ')');
13379     };
13380 })();
13381 /** 
13382  * Shorthand for {@link Roo.util.JSON#encode}
13383  * @member Roo encode 
13384  * @method */
13385 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13386 /** 
13387  * Shorthand for {@link Roo.util.JSON#decode}
13388  * @member Roo decode 
13389  * @method */
13390 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13391 /*
13392  * Based on:
13393  * Ext JS Library 1.1.1
13394  * Copyright(c) 2006-2007, Ext JS, LLC.
13395  *
13396  * Originally Released Under LGPL - original licence link has changed is not relivant.
13397  *
13398  * Fork - LGPL
13399  * <script type="text/javascript">
13400  */
13401  
13402 /**
13403  * @class Roo.util.Format
13404  * Reusable data formatting functions
13405  * @singleton
13406  */
13407 Roo.util.Format = function(){
13408     var trimRe = /^\s+|\s+$/g;
13409     return {
13410         /**
13411          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13412          * @param {String} value The string to truncate
13413          * @param {Number} length The maximum length to allow before truncating
13414          * @return {String} The converted text
13415          */
13416         ellipsis : function(value, len){
13417             if(value && value.length > len){
13418                 return value.substr(0, len-3)+"...";
13419             }
13420             return value;
13421         },
13422
13423         /**
13424          * Checks a reference and converts it to empty string if it is undefined
13425          * @param {Mixed} value Reference to check
13426          * @return {Mixed} Empty string if converted, otherwise the original value
13427          */
13428         undef : function(value){
13429             return typeof value != "undefined" ? value : "";
13430         },
13431
13432         /**
13433          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13434          * @param {String} value The string to encode
13435          * @return {String} The encoded text
13436          */
13437         htmlEncode : function(value){
13438             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13439         },
13440
13441         /**
13442          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13443          * @param {String} value The string to decode
13444          * @return {String} The decoded text
13445          */
13446         htmlDecode : function(value){
13447             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13448         },
13449
13450         /**
13451          * Trims any whitespace from either side of a string
13452          * @param {String} value The text to trim
13453          * @return {String} The trimmed text
13454          */
13455         trim : function(value){
13456             return String(value).replace(trimRe, "");
13457         },
13458
13459         /**
13460          * Returns a substring from within an original string
13461          * @param {String} value The original text
13462          * @param {Number} start The start index of the substring
13463          * @param {Number} length The length of the substring
13464          * @return {String} The substring
13465          */
13466         substr : function(value, start, length){
13467             return String(value).substr(start, length);
13468         },
13469
13470         /**
13471          * Converts a string to all lower case letters
13472          * @param {String} value The text to convert
13473          * @return {String} The converted text
13474          */
13475         lowercase : function(value){
13476             return String(value).toLowerCase();
13477         },
13478
13479         /**
13480          * Converts a string to all upper case letters
13481          * @param {String} value The text to convert
13482          * @return {String} The converted text
13483          */
13484         uppercase : function(value){
13485             return String(value).toUpperCase();
13486         },
13487
13488         /**
13489          * Converts the first character only of a string to upper case
13490          * @param {String} value The text to convert
13491          * @return {String} The converted text
13492          */
13493         capitalize : function(value){
13494             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13495         },
13496
13497         // private
13498         call : function(value, fn){
13499             if(arguments.length > 2){
13500                 var args = Array.prototype.slice.call(arguments, 2);
13501                 args.unshift(value);
13502                  
13503                 return /** eval:var:value */  eval(fn).apply(window, args);
13504             }else{
13505                 /** eval:var:value */
13506                 return /** eval:var:value */ eval(fn).call(window, value);
13507             }
13508         },
13509
13510        
13511         /**
13512          * safer version of Math.toFixed..??/
13513          * @param {Number/String} value The numeric value to format
13514          * @param {Number/String} value Decimal places 
13515          * @return {String} The formatted currency string
13516          */
13517         toFixed : function(v, n)
13518         {
13519             // why not use to fixed - precision is buggered???
13520             if (!n) {
13521                 return Math.round(v-0);
13522             }
13523             var fact = Math.pow(10,n+1);
13524             v = (Math.round((v-0)*fact))/fact;
13525             var z = (''+fact).substring(2);
13526             if (v == Math.floor(v)) {
13527                 return Math.floor(v) + '.' + z;
13528             }
13529             
13530             // now just padd decimals..
13531             var ps = String(v).split('.');
13532             var fd = (ps[1] + z);
13533             var r = fd.substring(0,n); 
13534             var rm = fd.substring(n); 
13535             if (rm < 5) {
13536                 return ps[0] + '.' + r;
13537             }
13538             r*=1; // turn it into a number;
13539             r++;
13540             if (String(r).length != n) {
13541                 ps[0]*=1;
13542                 ps[0]++;
13543                 r = String(r).substring(1); // chop the end off.
13544             }
13545             
13546             return ps[0] + '.' + r;
13547              
13548         },
13549         
13550         /**
13551          * Format a number as US currency
13552          * @param {Number/String} value The numeric value to format
13553          * @return {String} The formatted currency string
13554          */
13555         usMoney : function(v){
13556             return '$' + Roo.util.Format.number(v);
13557         },
13558         
13559         /**
13560          * Format a number
13561          * eventually this should probably emulate php's number_format
13562          * @param {Number/String} value The numeric value to format
13563          * @param {Number} decimals number of decimal places
13564          * @return {String} The formatted currency string
13565          */
13566         number : function(v,decimals)
13567         {
13568             // multiply and round.
13569             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13570             var mul = Math.pow(10, decimals);
13571             var zero = String(mul).substring(1);
13572             v = (Math.round((v-0)*mul))/mul;
13573             
13574             // if it's '0' number.. then
13575             
13576             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13577             v = String(v);
13578             var ps = v.split('.');
13579             var whole = ps[0];
13580             
13581             
13582             var r = /(\d+)(\d{3})/;
13583             // add comma's
13584             while (r.test(whole)) {
13585                 whole = whole.replace(r, '$1' + ',' + '$2');
13586             }
13587             
13588             
13589             var sub = ps[1] ?
13590                     // has decimals..
13591                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13592                     // does not have decimals
13593                     (decimals ? ('.' + zero) : '');
13594             
13595             
13596             return whole + sub ;
13597         },
13598         
13599         /**
13600          * Parse a value into a formatted date using the specified format pattern.
13601          * @param {Mixed} value The value to format
13602          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13603          * @return {String} The formatted date string
13604          */
13605         date : function(v, format){
13606             if(!v){
13607                 return "";
13608             }
13609             if(!(v instanceof Date)){
13610                 v = new Date(Date.parse(v));
13611             }
13612             return v.dateFormat(format || Roo.util.Format.defaults.date);
13613         },
13614
13615         /**
13616          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13617          * @param {String} format Any valid date format string
13618          * @return {Function} The date formatting function
13619          */
13620         dateRenderer : function(format){
13621             return function(v){
13622                 return Roo.util.Format.date(v, format);  
13623             };
13624         },
13625
13626         // private
13627         stripTagsRE : /<\/?[^>]+>/gi,
13628         
13629         /**
13630          * Strips all HTML tags
13631          * @param {Mixed} value The text from which to strip tags
13632          * @return {String} The stripped text
13633          */
13634         stripTags : function(v){
13635             return !v ? v : String(v).replace(this.stripTagsRE, "");
13636         }
13637     };
13638 }();
13639 Roo.util.Format.defaults = {
13640     date : 'd/M/Y'
13641 };/*
13642  * Based on:
13643  * Ext JS Library 1.1.1
13644  * Copyright(c) 2006-2007, Ext JS, LLC.
13645  *
13646  * Originally Released Under LGPL - original licence link has changed is not relivant.
13647  *
13648  * Fork - LGPL
13649  * <script type="text/javascript">
13650  */
13651
13652
13653  
13654
13655 /**
13656  * @class Roo.MasterTemplate
13657  * @extends Roo.Template
13658  * Provides a template that can have child templates. The syntax is:
13659 <pre><code>
13660 var t = new Roo.MasterTemplate(
13661         '&lt;select name="{name}"&gt;',
13662                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13663         '&lt;/select&gt;'
13664 );
13665 t.add('options', {value: 'foo', text: 'bar'});
13666 // or you can add multiple child elements in one shot
13667 t.addAll('options', [
13668     {value: 'foo', text: 'bar'},
13669     {value: 'foo2', text: 'bar2'},
13670     {value: 'foo3', text: 'bar3'}
13671 ]);
13672 // then append, applying the master template values
13673 t.append('my-form', {name: 'my-select'});
13674 </code></pre>
13675 * A name attribute for the child template is not required if you have only one child
13676 * template or you want to refer to them by index.
13677  */
13678 Roo.MasterTemplate = function(){
13679     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13680     this.originalHtml = this.html;
13681     var st = {};
13682     var m, re = this.subTemplateRe;
13683     re.lastIndex = 0;
13684     var subIndex = 0;
13685     while(m = re.exec(this.html)){
13686         var name = m[1], content = m[2];
13687         st[subIndex] = {
13688             name: name,
13689             index: subIndex,
13690             buffer: [],
13691             tpl : new Roo.Template(content)
13692         };
13693         if(name){
13694             st[name] = st[subIndex];
13695         }
13696         st[subIndex].tpl.compile();
13697         st[subIndex].tpl.call = this.call.createDelegate(this);
13698         subIndex++;
13699     }
13700     this.subCount = subIndex;
13701     this.subs = st;
13702 };
13703 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13704     /**
13705     * The regular expression used to match sub templates
13706     * @type RegExp
13707     * @property
13708     */
13709     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13710
13711     /**
13712      * Applies the passed values to a child template.
13713      * @param {String/Number} name (optional) The name or index of the child template
13714      * @param {Array/Object} values The values to be applied to the template
13715      * @return {MasterTemplate} this
13716      */
13717      add : function(name, values){
13718         if(arguments.length == 1){
13719             values = arguments[0];
13720             name = 0;
13721         }
13722         var s = this.subs[name];
13723         s.buffer[s.buffer.length] = s.tpl.apply(values);
13724         return this;
13725     },
13726
13727     /**
13728      * Applies all the passed values to a child template.
13729      * @param {String/Number} name (optional) The name or index of the child template
13730      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13731      * @param {Boolean} reset (optional) True to reset the template first
13732      * @return {MasterTemplate} this
13733      */
13734     fill : function(name, values, reset){
13735         var a = arguments;
13736         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13737             values = a[0];
13738             name = 0;
13739             reset = a[1];
13740         }
13741         if(reset){
13742             this.reset();
13743         }
13744         for(var i = 0, len = values.length; i < len; i++){
13745             this.add(name, values[i]);
13746         }
13747         return this;
13748     },
13749
13750     /**
13751      * Resets the template for reuse
13752      * @return {MasterTemplate} this
13753      */
13754      reset : function(){
13755         var s = this.subs;
13756         for(var i = 0; i < this.subCount; i++){
13757             s[i].buffer = [];
13758         }
13759         return this;
13760     },
13761
13762     applyTemplate : function(values){
13763         var s = this.subs;
13764         var replaceIndex = -1;
13765         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13766             return s[++replaceIndex].buffer.join("");
13767         });
13768         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13769     },
13770
13771     apply : function(){
13772         return this.applyTemplate.apply(this, arguments);
13773     },
13774
13775     compile : function(){return this;}
13776 });
13777
13778 /**
13779  * Alias for fill().
13780  * @method
13781  */
13782 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13783  /**
13784  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13785  * var tpl = Roo.MasterTemplate.from('element-id');
13786  * @param {String/HTMLElement} el
13787  * @param {Object} config
13788  * @static
13789  */
13790 Roo.MasterTemplate.from = function(el, config){
13791     el = Roo.getDom(el);
13792     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13793 };/*
13794  * Based on:
13795  * Ext JS Library 1.1.1
13796  * Copyright(c) 2006-2007, Ext JS, LLC.
13797  *
13798  * Originally Released Under LGPL - original licence link has changed is not relivant.
13799  *
13800  * Fork - LGPL
13801  * <script type="text/javascript">
13802  */
13803
13804  
13805 /**
13806  * @class Roo.util.CSS
13807  * Utility class for manipulating CSS rules
13808  * @singleton
13809  */
13810 Roo.util.CSS = function(){
13811         var rules = null;
13812         var doc = document;
13813
13814     var camelRe = /(-[a-z])/gi;
13815     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13816
13817    return {
13818    /**
13819     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13820     * tag and appended to the HEAD of the document.
13821     * @param {String|Object} cssText The text containing the css rules
13822     * @param {String} id An id to add to the stylesheet for later removal
13823     * @return {StyleSheet}
13824     */
13825     createStyleSheet : function(cssText, id){
13826         var ss;
13827         var head = doc.getElementsByTagName("head")[0];
13828         var nrules = doc.createElement("style");
13829         nrules.setAttribute("type", "text/css");
13830         if(id){
13831             nrules.setAttribute("id", id);
13832         }
13833         if (typeof(cssText) != 'string') {
13834             // support object maps..
13835             // not sure if this a good idea.. 
13836             // perhaps it should be merged with the general css handling
13837             // and handle js style props.
13838             var cssTextNew = [];
13839             for(var n in cssText) {
13840                 var citems = [];
13841                 for(var k in cssText[n]) {
13842                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13843                 }
13844                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13845                 
13846             }
13847             cssText = cssTextNew.join("\n");
13848             
13849         }
13850        
13851        
13852        if(Roo.isIE){
13853            head.appendChild(nrules);
13854            ss = nrules.styleSheet;
13855            ss.cssText = cssText;
13856        }else{
13857            try{
13858                 nrules.appendChild(doc.createTextNode(cssText));
13859            }catch(e){
13860                nrules.cssText = cssText; 
13861            }
13862            head.appendChild(nrules);
13863            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13864        }
13865        this.cacheStyleSheet(ss);
13866        return ss;
13867    },
13868
13869    /**
13870     * Removes a style or link tag by id
13871     * @param {String} id The id of the tag
13872     */
13873    removeStyleSheet : function(id){
13874        var existing = doc.getElementById(id);
13875        if(existing){
13876            existing.parentNode.removeChild(existing);
13877        }
13878    },
13879
13880    /**
13881     * Dynamically swaps an existing stylesheet reference for a new one
13882     * @param {String} id The id of an existing link tag to remove
13883     * @param {String} url The href of the new stylesheet to include
13884     */
13885    swapStyleSheet : function(id, url){
13886        this.removeStyleSheet(id);
13887        var ss = doc.createElement("link");
13888        ss.setAttribute("rel", "stylesheet");
13889        ss.setAttribute("type", "text/css");
13890        ss.setAttribute("id", id);
13891        ss.setAttribute("href", url);
13892        doc.getElementsByTagName("head")[0].appendChild(ss);
13893    },
13894    
13895    /**
13896     * Refresh the rule cache if you have dynamically added stylesheets
13897     * @return {Object} An object (hash) of rules indexed by selector
13898     */
13899    refreshCache : function(){
13900        return this.getRules(true);
13901    },
13902
13903    // private
13904    cacheStyleSheet : function(stylesheet){
13905        if(!rules){
13906            rules = {};
13907        }
13908        try{// try catch for cross domain access issue
13909            var ssRules = stylesheet.cssRules || stylesheet.rules;
13910            for(var j = ssRules.length-1; j >= 0; --j){
13911                rules[ssRules[j].selectorText] = ssRules[j];
13912            }
13913        }catch(e){}
13914    },
13915    
13916    /**
13917     * Gets all css rules for the document
13918     * @param {Boolean} refreshCache true to refresh the internal cache
13919     * @return {Object} An object (hash) of rules indexed by selector
13920     */
13921    getRules : function(refreshCache){
13922                 if(rules == null || refreshCache){
13923                         rules = {};
13924                         var ds = doc.styleSheets;
13925                         for(var i =0, len = ds.length; i < len; i++){
13926                             try{
13927                         this.cacheStyleSheet(ds[i]);
13928                     }catch(e){} 
13929                 }
13930                 }
13931                 return rules;
13932         },
13933         
13934         /**
13935     * Gets an an individual CSS rule by selector(s)
13936     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13937     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13938     * @return {CSSRule} The CSS rule or null if one is not found
13939     */
13940    getRule : function(selector, refreshCache){
13941                 var rs = this.getRules(refreshCache);
13942                 if(!(selector instanceof Array)){
13943                     return rs[selector];
13944                 }
13945                 for(var i = 0; i < selector.length; i++){
13946                         if(rs[selector[i]]){
13947                                 return rs[selector[i]];
13948                         }
13949                 }
13950                 return null;
13951         },
13952         
13953         
13954         /**
13955     * Updates a rule property
13956     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13957     * @param {String} property The css property
13958     * @param {String} value The new value for the property
13959     * @return {Boolean} true If a rule was found and updated
13960     */
13961    updateRule : function(selector, property, value){
13962                 if(!(selector instanceof Array)){
13963                         var rule = this.getRule(selector);
13964                         if(rule){
13965                                 rule.style[property.replace(camelRe, camelFn)] = value;
13966                                 return true;
13967                         }
13968                 }else{
13969                         for(var i = 0; i < selector.length; i++){
13970                                 if(this.updateRule(selector[i], property, value)){
13971                                         return true;
13972                                 }
13973                         }
13974                 }
13975                 return false;
13976         }
13977    };   
13978 }();/*
13979  * Based on:
13980  * Ext JS Library 1.1.1
13981  * Copyright(c) 2006-2007, Ext JS, LLC.
13982  *
13983  * Originally Released Under LGPL - original licence link has changed is not relivant.
13984  *
13985  * Fork - LGPL
13986  * <script type="text/javascript">
13987  */
13988
13989  
13990
13991 /**
13992  * @class Roo.util.ClickRepeater
13993  * @extends Roo.util.Observable
13994  * 
13995  * A wrapper class which can be applied to any element. Fires a "click" event while the
13996  * mouse is pressed. The interval between firings may be specified in the config but
13997  * defaults to 10 milliseconds.
13998  * 
13999  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14000  * 
14001  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14002  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14003  * Similar to an autorepeat key delay.
14004  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14005  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14006  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14007  *           "interval" and "delay" are ignored. "immediate" is honored.
14008  * @cfg {Boolean} preventDefault True to prevent the default click event
14009  * @cfg {Boolean} stopDefault True to stop the default click event
14010  * 
14011  * @history
14012  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14013  *     2007-02-02 jvs Renamed to ClickRepeater
14014  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14015  *
14016  *  @constructor
14017  * @param {String/HTMLElement/Element} el The element to listen on
14018  * @param {Object} config
14019  **/
14020 Roo.util.ClickRepeater = function(el, config)
14021 {
14022     this.el = Roo.get(el);
14023     this.el.unselectable();
14024
14025     Roo.apply(this, config);
14026
14027     this.addEvents({
14028     /**
14029      * @event mousedown
14030      * Fires when the mouse button is depressed.
14031      * @param {Roo.util.ClickRepeater} this
14032      */
14033         "mousedown" : true,
14034     /**
14035      * @event click
14036      * Fires on a specified interval during the time the element is pressed.
14037      * @param {Roo.util.ClickRepeater} this
14038      */
14039         "click" : true,
14040     /**
14041      * @event mouseup
14042      * Fires when the mouse key is released.
14043      * @param {Roo.util.ClickRepeater} this
14044      */
14045         "mouseup" : true
14046     });
14047
14048     this.el.on("mousedown", this.handleMouseDown, this);
14049     if(this.preventDefault || this.stopDefault){
14050         this.el.on("click", function(e){
14051             if(this.preventDefault){
14052                 e.preventDefault();
14053             }
14054             if(this.stopDefault){
14055                 e.stopEvent();
14056             }
14057         }, this);
14058     }
14059
14060     // allow inline handler
14061     if(this.handler){
14062         this.on("click", this.handler,  this.scope || this);
14063     }
14064
14065     Roo.util.ClickRepeater.superclass.constructor.call(this);
14066 };
14067
14068 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14069     interval : 20,
14070     delay: 250,
14071     preventDefault : true,
14072     stopDefault : false,
14073     timer : 0,
14074
14075     // private
14076     handleMouseDown : function(){
14077         clearTimeout(this.timer);
14078         this.el.blur();
14079         if(this.pressClass){
14080             this.el.addClass(this.pressClass);
14081         }
14082         this.mousedownTime = new Date();
14083
14084         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14085         this.el.on("mouseout", this.handleMouseOut, this);
14086
14087         this.fireEvent("mousedown", this);
14088         this.fireEvent("click", this);
14089         
14090         this.timer = this.click.defer(this.delay || this.interval, this);
14091     },
14092
14093     // private
14094     click : function(){
14095         this.fireEvent("click", this);
14096         this.timer = this.click.defer(this.getInterval(), this);
14097     },
14098
14099     // private
14100     getInterval: function(){
14101         if(!this.accelerate){
14102             return this.interval;
14103         }
14104         var pressTime = this.mousedownTime.getElapsed();
14105         if(pressTime < 500){
14106             return 400;
14107         }else if(pressTime < 1700){
14108             return 320;
14109         }else if(pressTime < 2600){
14110             return 250;
14111         }else if(pressTime < 3500){
14112             return 180;
14113         }else if(pressTime < 4400){
14114             return 140;
14115         }else if(pressTime < 5300){
14116             return 80;
14117         }else if(pressTime < 6200){
14118             return 50;
14119         }else{
14120             return 10;
14121         }
14122     },
14123
14124     // private
14125     handleMouseOut : function(){
14126         clearTimeout(this.timer);
14127         if(this.pressClass){
14128             this.el.removeClass(this.pressClass);
14129         }
14130         this.el.on("mouseover", this.handleMouseReturn, this);
14131     },
14132
14133     // private
14134     handleMouseReturn : function(){
14135         this.el.un("mouseover", this.handleMouseReturn);
14136         if(this.pressClass){
14137             this.el.addClass(this.pressClass);
14138         }
14139         this.click();
14140     },
14141
14142     // private
14143     handleMouseUp : function(){
14144         clearTimeout(this.timer);
14145         this.el.un("mouseover", this.handleMouseReturn);
14146         this.el.un("mouseout", this.handleMouseOut);
14147         Roo.get(document).un("mouseup", this.handleMouseUp);
14148         this.el.removeClass(this.pressClass);
14149         this.fireEvent("mouseup", this);
14150     }
14151 });/*
14152  * Based on:
14153  * Ext JS Library 1.1.1
14154  * Copyright(c) 2006-2007, Ext JS, LLC.
14155  *
14156  * Originally Released Under LGPL - original licence link has changed is not relivant.
14157  *
14158  * Fork - LGPL
14159  * <script type="text/javascript">
14160  */
14161
14162  
14163 /**
14164  * @class Roo.KeyNav
14165  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14166  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14167  * way to implement custom navigation schemes for any UI component.</p>
14168  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14169  * pageUp, pageDown, del, home, end.  Usage:</p>
14170  <pre><code>
14171 var nav = new Roo.KeyNav("my-element", {
14172     "left" : function(e){
14173         this.moveLeft(e.ctrlKey);
14174     },
14175     "right" : function(e){
14176         this.moveRight(e.ctrlKey);
14177     },
14178     "enter" : function(e){
14179         this.save();
14180     },
14181     scope : this
14182 });
14183 </code></pre>
14184  * @constructor
14185  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14186  * @param {Object} config The config
14187  */
14188 Roo.KeyNav = function(el, config){
14189     this.el = Roo.get(el);
14190     Roo.apply(this, config);
14191     if(!this.disabled){
14192         this.disabled = true;
14193         this.enable();
14194     }
14195 };
14196
14197 Roo.KeyNav.prototype = {
14198     /**
14199      * @cfg {Boolean} disabled
14200      * True to disable this KeyNav instance (defaults to false)
14201      */
14202     disabled : false,
14203     /**
14204      * @cfg {String} defaultEventAction
14205      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14206      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14207      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14208      */
14209     defaultEventAction: "stopEvent",
14210     /**
14211      * @cfg {Boolean} forceKeyDown
14212      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14213      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14214      * handle keydown instead of keypress.
14215      */
14216     forceKeyDown : false,
14217
14218     // private
14219     prepareEvent : function(e){
14220         var k = e.getKey();
14221         var h = this.keyToHandler[k];
14222         //if(h && this[h]){
14223         //    e.stopPropagation();
14224         //}
14225         if(Roo.isSafari && h && k >= 37 && k <= 40){
14226             e.stopEvent();
14227         }
14228     },
14229
14230     // private
14231     relay : function(e){
14232         var k = e.getKey();
14233         var h = this.keyToHandler[k];
14234         if(h && this[h]){
14235             if(this.doRelay(e, this[h], h) !== true){
14236                 e[this.defaultEventAction]();
14237             }
14238         }
14239     },
14240
14241     // private
14242     doRelay : function(e, h, hname){
14243         return h.call(this.scope || this, e);
14244     },
14245
14246     // possible handlers
14247     enter : false,
14248     left : false,
14249     right : false,
14250     up : false,
14251     down : false,
14252     tab : false,
14253     esc : false,
14254     pageUp : false,
14255     pageDown : false,
14256     del : false,
14257     home : false,
14258     end : false,
14259
14260     // quick lookup hash
14261     keyToHandler : {
14262         37 : "left",
14263         39 : "right",
14264         38 : "up",
14265         40 : "down",
14266         33 : "pageUp",
14267         34 : "pageDown",
14268         46 : "del",
14269         36 : "home",
14270         35 : "end",
14271         13 : "enter",
14272         27 : "esc",
14273         9  : "tab"
14274     },
14275
14276         /**
14277          * Enable this KeyNav
14278          */
14279         enable: function(){
14280                 if(this.disabled){
14281             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14282             // the EventObject will normalize Safari automatically
14283             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14284                 this.el.on("keydown", this.relay,  this);
14285             }else{
14286                 this.el.on("keydown", this.prepareEvent,  this);
14287                 this.el.on("keypress", this.relay,  this);
14288             }
14289                     this.disabled = false;
14290                 }
14291         },
14292
14293         /**
14294          * Disable this KeyNav
14295          */
14296         disable: function(){
14297                 if(!this.disabled){
14298                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14299                 this.el.un("keydown", this.relay);
14300             }else{
14301                 this.el.un("keydown", this.prepareEvent);
14302                 this.el.un("keypress", this.relay);
14303             }
14304                     this.disabled = true;
14305                 }
14306         }
14307 };/*
14308  * Based on:
14309  * Ext JS Library 1.1.1
14310  * Copyright(c) 2006-2007, Ext JS, LLC.
14311  *
14312  * Originally Released Under LGPL - original licence link has changed is not relivant.
14313  *
14314  * Fork - LGPL
14315  * <script type="text/javascript">
14316  */
14317
14318  
14319 /**
14320  * @class Roo.KeyMap
14321  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14322  * The constructor accepts the same config object as defined by {@link #addBinding}.
14323  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14324  * combination it will call the function with this signature (if the match is a multi-key
14325  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14326  * A KeyMap can also handle a string representation of keys.<br />
14327  * Usage:
14328  <pre><code>
14329 // map one key by key code
14330 var map = new Roo.KeyMap("my-element", {
14331     key: 13, // or Roo.EventObject.ENTER
14332     fn: myHandler,
14333     scope: myObject
14334 });
14335
14336 // map multiple keys to one action by string
14337 var map = new Roo.KeyMap("my-element", {
14338     key: "a\r\n\t",
14339     fn: myHandler,
14340     scope: myObject
14341 });
14342
14343 // map multiple keys to multiple actions by strings and array of codes
14344 var map = new Roo.KeyMap("my-element", [
14345     {
14346         key: [10,13],
14347         fn: function(){ alert("Return was pressed"); }
14348     }, {
14349         key: "abc",
14350         fn: function(){ alert('a, b or c was pressed'); }
14351     }, {
14352         key: "\t",
14353         ctrl:true,
14354         shift:true,
14355         fn: function(){ alert('Control + shift + tab was pressed.'); }
14356     }
14357 ]);
14358 </code></pre>
14359  * <b>Note: A KeyMap starts enabled</b>
14360  * @constructor
14361  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14362  * @param {Object} config The config (see {@link #addBinding})
14363  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14364  */
14365 Roo.KeyMap = function(el, config, eventName){
14366     this.el  = Roo.get(el);
14367     this.eventName = eventName || "keydown";
14368     this.bindings = [];
14369     if(config){
14370         this.addBinding(config);
14371     }
14372     this.enable();
14373 };
14374
14375 Roo.KeyMap.prototype = {
14376     /**
14377      * True to stop the event from bubbling and prevent the default browser action if the
14378      * key was handled by the KeyMap (defaults to false)
14379      * @type Boolean
14380      */
14381     stopEvent : false,
14382
14383     /**
14384      * Add a new binding to this KeyMap. The following config object properties are supported:
14385      * <pre>
14386 Property    Type             Description
14387 ----------  ---------------  ----------------------------------------------------------------------
14388 key         String/Array     A single keycode or an array of keycodes to handle
14389 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14390 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14391 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14392 fn          Function         The function to call when KeyMap finds the expected key combination
14393 scope       Object           The scope of the callback function
14394 </pre>
14395      *
14396      * Usage:
14397      * <pre><code>
14398 // Create a KeyMap
14399 var map = new Roo.KeyMap(document, {
14400     key: Roo.EventObject.ENTER,
14401     fn: handleKey,
14402     scope: this
14403 });
14404
14405 //Add a new binding to the existing KeyMap later
14406 map.addBinding({
14407     key: 'abc',
14408     shift: true,
14409     fn: handleKey,
14410     scope: this
14411 });
14412 </code></pre>
14413      * @param {Object/Array} config A single KeyMap config or an array of configs
14414      */
14415         addBinding : function(config){
14416         if(config instanceof Array){
14417             for(var i = 0, len = config.length; i < len; i++){
14418                 this.addBinding(config[i]);
14419             }
14420             return;
14421         }
14422         var keyCode = config.key,
14423             shift = config.shift, 
14424             ctrl = config.ctrl, 
14425             alt = config.alt,
14426             fn = config.fn,
14427             scope = config.scope;
14428         if(typeof keyCode == "string"){
14429             var ks = [];
14430             var keyString = keyCode.toUpperCase();
14431             for(var j = 0, len = keyString.length; j < len; j++){
14432                 ks.push(keyString.charCodeAt(j));
14433             }
14434             keyCode = ks;
14435         }
14436         var keyArray = keyCode instanceof Array;
14437         var handler = function(e){
14438             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14439                 var k = e.getKey();
14440                 if(keyArray){
14441                     for(var i = 0, len = keyCode.length; i < len; i++){
14442                         if(keyCode[i] == k){
14443                           if(this.stopEvent){
14444                               e.stopEvent();
14445                           }
14446                           fn.call(scope || window, k, e);
14447                           return;
14448                         }
14449                     }
14450                 }else{
14451                     if(k == keyCode){
14452                         if(this.stopEvent){
14453                            e.stopEvent();
14454                         }
14455                         fn.call(scope || window, k, e);
14456                     }
14457                 }
14458             }
14459         };
14460         this.bindings.push(handler);  
14461         },
14462
14463     /**
14464      * Shorthand for adding a single key listener
14465      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14466      * following options:
14467      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14468      * @param {Function} fn The function to call
14469      * @param {Object} scope (optional) The scope of the function
14470      */
14471     on : function(key, fn, scope){
14472         var keyCode, shift, ctrl, alt;
14473         if(typeof key == "object" && !(key instanceof Array)){
14474             keyCode = key.key;
14475             shift = key.shift;
14476             ctrl = key.ctrl;
14477             alt = key.alt;
14478         }else{
14479             keyCode = key;
14480         }
14481         this.addBinding({
14482             key: keyCode,
14483             shift: shift,
14484             ctrl: ctrl,
14485             alt: alt,
14486             fn: fn,
14487             scope: scope
14488         })
14489     },
14490
14491     // private
14492     handleKeyDown : function(e){
14493             if(this.enabled){ //just in case
14494             var b = this.bindings;
14495             for(var i = 0, len = b.length; i < len; i++){
14496                 b[i].call(this, e);
14497             }
14498             }
14499         },
14500         
14501         /**
14502          * Returns true if this KeyMap is enabled
14503          * @return {Boolean} 
14504          */
14505         isEnabled : function(){
14506             return this.enabled;  
14507         },
14508         
14509         /**
14510          * Enables this KeyMap
14511          */
14512         enable: function(){
14513                 if(!this.enabled){
14514                     this.el.on(this.eventName, this.handleKeyDown, this);
14515                     this.enabled = true;
14516                 }
14517         },
14518
14519         /**
14520          * Disable this KeyMap
14521          */
14522         disable: function(){
14523                 if(this.enabled){
14524                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14525                     this.enabled = false;
14526                 }
14527         }
14528 };/*
14529  * Based on:
14530  * Ext JS Library 1.1.1
14531  * Copyright(c) 2006-2007, Ext JS, LLC.
14532  *
14533  * Originally Released Under LGPL - original licence link has changed is not relivant.
14534  *
14535  * Fork - LGPL
14536  * <script type="text/javascript">
14537  */
14538
14539  
14540 /**
14541  * @class Roo.util.TextMetrics
14542  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14543  * wide, in pixels, a given block of text will be.
14544  * @singleton
14545  */
14546 Roo.util.TextMetrics = function(){
14547     var shared;
14548     return {
14549         /**
14550          * Measures the size of the specified text
14551          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14552          * that can affect the size of the rendered text
14553          * @param {String} text The text to measure
14554          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14555          * in order to accurately measure the text height
14556          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14557          */
14558         measure : function(el, text, fixedWidth){
14559             if(!shared){
14560                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14561             }
14562             shared.bind(el);
14563             shared.setFixedWidth(fixedWidth || 'auto');
14564             return shared.getSize(text);
14565         },
14566
14567         /**
14568          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14569          * the overhead of multiple calls to initialize the style properties on each measurement.
14570          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14571          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14572          * in order to accurately measure the text height
14573          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14574          */
14575         createInstance : function(el, fixedWidth){
14576             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14577         }
14578     };
14579 }();
14580
14581  
14582
14583 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14584     var ml = new Roo.Element(document.createElement('div'));
14585     document.body.appendChild(ml.dom);
14586     ml.position('absolute');
14587     ml.setLeftTop(-1000, -1000);
14588     ml.hide();
14589
14590     if(fixedWidth){
14591         ml.setWidth(fixedWidth);
14592     }
14593      
14594     var instance = {
14595         /**
14596          * Returns the size of the specified text based on the internal element's style and width properties
14597          * @memberOf Roo.util.TextMetrics.Instance#
14598          * @param {String} text The text to measure
14599          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14600          */
14601         getSize : function(text){
14602             ml.update(text);
14603             var s = ml.getSize();
14604             ml.update('');
14605             return s;
14606         },
14607
14608         /**
14609          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14610          * that can affect the size of the rendered text
14611          * @memberOf Roo.util.TextMetrics.Instance#
14612          * @param {String/HTMLElement} el The element, dom node or id
14613          */
14614         bind : function(el){
14615             ml.setStyle(
14616                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14617             );
14618         },
14619
14620         /**
14621          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14622          * to set a fixed width in order to accurately measure the text height.
14623          * @memberOf Roo.util.TextMetrics.Instance#
14624          * @param {Number} width The width to set on the element
14625          */
14626         setFixedWidth : function(width){
14627             ml.setWidth(width);
14628         },
14629
14630         /**
14631          * Returns the measured width of the specified text
14632          * @memberOf Roo.util.TextMetrics.Instance#
14633          * @param {String} text The text to measure
14634          * @return {Number} width The width in pixels
14635          */
14636         getWidth : function(text){
14637             ml.dom.style.width = 'auto';
14638             return this.getSize(text).width;
14639         },
14640
14641         /**
14642          * Returns the measured height of the specified text.  For multiline text, be sure to call
14643          * {@link #setFixedWidth} if necessary.
14644          * @memberOf Roo.util.TextMetrics.Instance#
14645          * @param {String} text The text to measure
14646          * @return {Number} height The height in pixels
14647          */
14648         getHeight : function(text){
14649             return this.getSize(text).height;
14650         }
14651     };
14652
14653     instance.bind(bindTo);
14654
14655     return instance;
14656 };
14657
14658 // backwards compat
14659 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14660  * Based on:
14661  * Ext JS Library 1.1.1
14662  * Copyright(c) 2006-2007, Ext JS, LLC.
14663  *
14664  * Originally Released Under LGPL - original licence link has changed is not relivant.
14665  *
14666  * Fork - LGPL
14667  * <script type="text/javascript">
14668  */
14669
14670 /**
14671  * @class Roo.state.Provider
14672  * Abstract base class for state provider implementations. This class provides methods
14673  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14674  * Provider interface.
14675  */
14676 Roo.state.Provider = function(){
14677     /**
14678      * @event statechange
14679      * Fires when a state change occurs.
14680      * @param {Provider} this This state provider
14681      * @param {String} key The state key which was changed
14682      * @param {String} value The encoded value for the state
14683      */
14684     this.addEvents({
14685         "statechange": true
14686     });
14687     this.state = {};
14688     Roo.state.Provider.superclass.constructor.call(this);
14689 };
14690 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14691     /**
14692      * Returns the current value for a key
14693      * @param {String} name The key name
14694      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14695      * @return {Mixed} The state data
14696      */
14697     get : function(name, defaultValue){
14698         return typeof this.state[name] == "undefined" ?
14699             defaultValue : this.state[name];
14700     },
14701     
14702     /**
14703      * Clears a value from the state
14704      * @param {String} name The key name
14705      */
14706     clear : function(name){
14707         delete this.state[name];
14708         this.fireEvent("statechange", this, name, null);
14709     },
14710     
14711     /**
14712      * Sets the value for a key
14713      * @param {String} name The key name
14714      * @param {Mixed} value The value to set
14715      */
14716     set : function(name, value){
14717         this.state[name] = value;
14718         this.fireEvent("statechange", this, name, value);
14719     },
14720     
14721     /**
14722      * Decodes a string previously encoded with {@link #encodeValue}.
14723      * @param {String} value The value to decode
14724      * @return {Mixed} The decoded value
14725      */
14726     decodeValue : function(cookie){
14727         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14728         var matches = re.exec(unescape(cookie));
14729         if(!matches || !matches[1]) return; // non state cookie
14730         var type = matches[1];
14731         var v = matches[2];
14732         switch(type){
14733             case "n":
14734                 return parseFloat(v);
14735             case "d":
14736                 return new Date(Date.parse(v));
14737             case "b":
14738                 return (v == "1");
14739             case "a":
14740                 var all = [];
14741                 var values = v.split("^");
14742                 for(var i = 0, len = values.length; i < len; i++){
14743                     all.push(this.decodeValue(values[i]));
14744                 }
14745                 return all;
14746            case "o":
14747                 var all = {};
14748                 var values = v.split("^");
14749                 for(var i = 0, len = values.length; i < len; i++){
14750                     var kv = values[i].split("=");
14751                     all[kv[0]] = this.decodeValue(kv[1]);
14752                 }
14753                 return all;
14754            default:
14755                 return v;
14756         }
14757     },
14758     
14759     /**
14760      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14761      * @param {Mixed} value The value to encode
14762      * @return {String} The encoded value
14763      */
14764     encodeValue : function(v){
14765         var enc;
14766         if(typeof v == "number"){
14767             enc = "n:" + v;
14768         }else if(typeof v == "boolean"){
14769             enc = "b:" + (v ? "1" : "0");
14770         }else if(v instanceof Date){
14771             enc = "d:" + v.toGMTString();
14772         }else if(v instanceof Array){
14773             var flat = "";
14774             for(var i = 0, len = v.length; i < len; i++){
14775                 flat += this.encodeValue(v[i]);
14776                 if(i != len-1) flat += "^";
14777             }
14778             enc = "a:" + flat;
14779         }else if(typeof v == "object"){
14780             var flat = "";
14781             for(var key in v){
14782                 if(typeof v[key] != "function"){
14783                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14784                 }
14785             }
14786             enc = "o:" + flat.substring(0, flat.length-1);
14787         }else{
14788             enc = "s:" + v;
14789         }
14790         return escape(enc);        
14791     }
14792 });
14793
14794 /*
14795  * Based on:
14796  * Ext JS Library 1.1.1
14797  * Copyright(c) 2006-2007, Ext JS, LLC.
14798  *
14799  * Originally Released Under LGPL - original licence link has changed is not relivant.
14800  *
14801  * Fork - LGPL
14802  * <script type="text/javascript">
14803  */
14804 /**
14805  * @class Roo.state.Manager
14806  * This is the global state manager. By default all components that are "state aware" check this class
14807  * for state information if you don't pass them a custom state provider. In order for this class
14808  * to be useful, it must be initialized with a provider when your application initializes.
14809  <pre><code>
14810 // in your initialization function
14811 init : function(){
14812    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14813    ...
14814    // supposed you have a {@link Roo.BorderLayout}
14815    var layout = new Roo.BorderLayout(...);
14816    layout.restoreState();
14817    // or a {Roo.BasicDialog}
14818    var dialog = new Roo.BasicDialog(...);
14819    dialog.restoreState();
14820  </code></pre>
14821  * @singleton
14822  */
14823 Roo.state.Manager = function(){
14824     var provider = new Roo.state.Provider();
14825     
14826     return {
14827         /**
14828          * Configures the default state provider for your application
14829          * @param {Provider} stateProvider The state provider to set
14830          */
14831         setProvider : function(stateProvider){
14832             provider = stateProvider;
14833         },
14834         
14835         /**
14836          * Returns the current value for a key
14837          * @param {String} name The key name
14838          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14839          * @return {Mixed} The state data
14840          */
14841         get : function(key, defaultValue){
14842             return provider.get(key, defaultValue);
14843         },
14844         
14845         /**
14846          * Sets the value for a key
14847          * @param {String} name The key name
14848          * @param {Mixed} value The state data
14849          */
14850          set : function(key, value){
14851             provider.set(key, value);
14852         },
14853         
14854         /**
14855          * Clears a value from the state
14856          * @param {String} name The key name
14857          */
14858         clear : function(key){
14859             provider.clear(key);
14860         },
14861         
14862         /**
14863          * Gets the currently configured state provider
14864          * @return {Provider} The state provider
14865          */
14866         getProvider : function(){
14867             return provider;
14868         }
14869     };
14870 }();
14871 /*
14872  * Based on:
14873  * Ext JS Library 1.1.1
14874  * Copyright(c) 2006-2007, Ext JS, LLC.
14875  *
14876  * Originally Released Under LGPL - original licence link has changed is not relivant.
14877  *
14878  * Fork - LGPL
14879  * <script type="text/javascript">
14880  */
14881 /**
14882  * @class Roo.state.CookieProvider
14883  * @extends Roo.state.Provider
14884  * The default Provider implementation which saves state via cookies.
14885  * <br />Usage:
14886  <pre><code>
14887    var cp = new Roo.state.CookieProvider({
14888        path: "/cgi-bin/",
14889        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14890        domain: "roojs.com"
14891    })
14892    Roo.state.Manager.setProvider(cp);
14893  </code></pre>
14894  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14895  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14896  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14897  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14898  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14899  * domain the page is running on including the 'www' like 'www.roojs.com')
14900  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14901  * @constructor
14902  * Create a new CookieProvider
14903  * @param {Object} config The configuration object
14904  */
14905 Roo.state.CookieProvider = function(config){
14906     Roo.state.CookieProvider.superclass.constructor.call(this);
14907     this.path = "/";
14908     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14909     this.domain = null;
14910     this.secure = false;
14911     Roo.apply(this, config);
14912     this.state = this.readCookies();
14913 };
14914
14915 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14916     // private
14917     set : function(name, value){
14918         if(typeof value == "undefined" || value === null){
14919             this.clear(name);
14920             return;
14921         }
14922         this.setCookie(name, value);
14923         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14924     },
14925
14926     // private
14927     clear : function(name){
14928         this.clearCookie(name);
14929         Roo.state.CookieProvider.superclass.clear.call(this, name);
14930     },
14931
14932     // private
14933     readCookies : function(){
14934         var cookies = {};
14935         var c = document.cookie + ";";
14936         var re = /\s?(.*?)=(.*?);/g;
14937         var matches;
14938         while((matches = re.exec(c)) != null){
14939             var name = matches[1];
14940             var value = matches[2];
14941             if(name && name.substring(0,3) == "ys-"){
14942                 cookies[name.substr(3)] = this.decodeValue(value);
14943             }
14944         }
14945         return cookies;
14946     },
14947
14948     // private
14949     setCookie : function(name, value){
14950         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14951            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14952            ((this.path == null) ? "" : ("; path=" + this.path)) +
14953            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14954            ((this.secure == true) ? "; secure" : "");
14955     },
14956
14957     // private
14958     clearCookie : function(name){
14959         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14960            ((this.path == null) ? "" : ("; path=" + this.path)) +
14961            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14962            ((this.secure == true) ? "; secure" : "");
14963     }
14964 });/*
14965  * Based on:
14966  * Ext JS Library 1.1.1
14967  * Copyright(c) 2006-2007, Ext JS, LLC.
14968  *
14969  * Originally Released Under LGPL - original licence link has changed is not relivant.
14970  *
14971  * Fork - LGPL
14972  * <script type="text/javascript">
14973  */
14974  
14975
14976 /**
14977  * @class Roo.ComponentMgr
14978  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14979  * @singleton
14980  */
14981 Roo.ComponentMgr = function(){
14982     var all = new Roo.util.MixedCollection();
14983
14984     return {
14985         /**
14986          * Registers a component.
14987          * @param {Roo.Component} c The component
14988          */
14989         register : function(c){
14990             all.add(c);
14991         },
14992
14993         /**
14994          * Unregisters a component.
14995          * @param {Roo.Component} c The component
14996          */
14997         unregister : function(c){
14998             all.remove(c);
14999         },
15000
15001         /**
15002          * Returns a component by id
15003          * @param {String} id The component id
15004          */
15005         get : function(id){
15006             return all.get(id);
15007         },
15008
15009         /**
15010          * Registers a function that will be called when a specified component is added to ComponentMgr
15011          * @param {String} id The component id
15012          * @param {Funtction} fn The callback function
15013          * @param {Object} scope The scope of the callback
15014          */
15015         onAvailable : function(id, fn, scope){
15016             all.on("add", function(index, o){
15017                 if(o.id == id){
15018                     fn.call(scope || o, o);
15019                     all.un("add", fn, scope);
15020                 }
15021             });
15022         }
15023     };
15024 }();/*
15025  * Based on:
15026  * Ext JS Library 1.1.1
15027  * Copyright(c) 2006-2007, Ext JS, LLC.
15028  *
15029  * Originally Released Under LGPL - original licence link has changed is not relivant.
15030  *
15031  * Fork - LGPL
15032  * <script type="text/javascript">
15033  */
15034  
15035 /**
15036  * @class Roo.Component
15037  * @extends Roo.util.Observable
15038  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15039  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15040  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15041  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15042  * All visual components (widgets) that require rendering into a layout should subclass Component.
15043  * @constructor
15044  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15045  * 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
15046  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15047  */
15048 Roo.Component = function(config){
15049     config = config || {};
15050     if(config.tagName || config.dom || typeof config == "string"){ // element object
15051         config = {el: config, id: config.id || config};
15052     }
15053     this.initialConfig = config;
15054
15055     Roo.apply(this, config);
15056     this.addEvents({
15057         /**
15058          * @event disable
15059          * Fires after the component is disabled.
15060              * @param {Roo.Component} this
15061              */
15062         disable : true,
15063         /**
15064          * @event enable
15065          * Fires after the component is enabled.
15066              * @param {Roo.Component} this
15067              */
15068         enable : true,
15069         /**
15070          * @event beforeshow
15071          * Fires before the component is shown.  Return false to stop the show.
15072              * @param {Roo.Component} this
15073              */
15074         beforeshow : true,
15075         /**
15076          * @event show
15077          * Fires after the component is shown.
15078              * @param {Roo.Component} this
15079              */
15080         show : true,
15081         /**
15082          * @event beforehide
15083          * Fires before the component is hidden. Return false to stop the hide.
15084              * @param {Roo.Component} this
15085              */
15086         beforehide : true,
15087         /**
15088          * @event hide
15089          * Fires after the component is hidden.
15090              * @param {Roo.Component} this
15091              */
15092         hide : true,
15093         /**
15094          * @event beforerender
15095          * Fires before the component is rendered. Return false to stop the render.
15096              * @param {Roo.Component} this
15097              */
15098         beforerender : true,
15099         /**
15100          * @event render
15101          * Fires after the component is rendered.
15102              * @param {Roo.Component} this
15103              */
15104         render : true,
15105         /**
15106          * @event beforedestroy
15107          * Fires before the component is destroyed. Return false to stop the destroy.
15108              * @param {Roo.Component} this
15109              */
15110         beforedestroy : true,
15111         /**
15112          * @event destroy
15113          * Fires after the component is destroyed.
15114              * @param {Roo.Component} this
15115              */
15116         destroy : true
15117     });
15118     if(!this.id){
15119         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15120     }
15121     Roo.ComponentMgr.register(this);
15122     Roo.Component.superclass.constructor.call(this);
15123     this.initComponent();
15124     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15125         this.render(this.renderTo);
15126         delete this.renderTo;
15127     }
15128 };
15129
15130 /** @private */
15131 Roo.Component.AUTO_ID = 1000;
15132
15133 Roo.extend(Roo.Component, Roo.util.Observable, {
15134     /**
15135      * @scope Roo.Component.prototype
15136      * @type {Boolean}
15137      * true if this component is hidden. Read-only.
15138      */
15139     hidden : false,
15140     /**
15141      * @type {Boolean}
15142      * true if this component is disabled. Read-only.
15143      */
15144     disabled : false,
15145     /**
15146      * @type {Boolean}
15147      * true if this component has been rendered. Read-only.
15148      */
15149     rendered : false,
15150     
15151     /** @cfg {String} disableClass
15152      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15153      */
15154     disabledClass : "x-item-disabled",
15155         /** @cfg {Boolean} allowDomMove
15156          * Whether the component can move the Dom node when rendering (defaults to true).
15157          */
15158     allowDomMove : true,
15159     /** @cfg {String} hideMode
15160      * How this component should hidden. Supported values are
15161      * "visibility" (css visibility), "offsets" (negative offset position) and
15162      * "display" (css display) - defaults to "display".
15163      */
15164     hideMode: 'display',
15165
15166     /** @private */
15167     ctype : "Roo.Component",
15168
15169     /**
15170      * @cfg {String} actionMode 
15171      * which property holds the element that used for  hide() / show() / disable() / enable()
15172      * default is 'el' 
15173      */
15174     actionMode : "el",
15175
15176     /** @private */
15177     getActionEl : function(){
15178         return this[this.actionMode];
15179     },
15180
15181     initComponent : Roo.emptyFn,
15182     /**
15183      * If this is a lazy rendering component, render it to its container element.
15184      * @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.
15185      */
15186     render : function(container, position){
15187         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15188             if(!container && this.el){
15189                 this.el = Roo.get(this.el);
15190                 container = this.el.dom.parentNode;
15191                 this.allowDomMove = false;
15192             }
15193             this.container = Roo.get(container);
15194             this.rendered = true;
15195             if(position !== undefined){
15196                 if(typeof position == 'number'){
15197                     position = this.container.dom.childNodes[position];
15198                 }else{
15199                     position = Roo.getDom(position);
15200                 }
15201             }
15202             this.onRender(this.container, position || null);
15203             if(this.cls){
15204                 this.el.addClass(this.cls);
15205                 delete this.cls;
15206             }
15207             if(this.style){
15208                 this.el.applyStyles(this.style);
15209                 delete this.style;
15210             }
15211             this.fireEvent("render", this);
15212             this.afterRender(this.container);
15213             if(this.hidden){
15214                 this.hide();
15215             }
15216             if(this.disabled){
15217                 this.disable();
15218             }
15219         }
15220         return this;
15221     },
15222
15223     /** @private */
15224     // default function is not really useful
15225     onRender : function(ct, position){
15226         if(this.el){
15227             this.el = Roo.get(this.el);
15228             if(this.allowDomMove !== false){
15229                 ct.dom.insertBefore(this.el.dom, position);
15230             }
15231         }
15232     },
15233
15234     /** @private */
15235     getAutoCreate : function(){
15236         var cfg = typeof this.autoCreate == "object" ?
15237                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15238         if(this.id && !cfg.id){
15239             cfg.id = this.id;
15240         }
15241         return cfg;
15242     },
15243
15244     /** @private */
15245     afterRender : Roo.emptyFn,
15246
15247     /**
15248      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15249      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15250      */
15251     destroy : function(){
15252         if(this.fireEvent("beforedestroy", this) !== false){
15253             this.purgeListeners();
15254             this.beforeDestroy();
15255             if(this.rendered){
15256                 this.el.removeAllListeners();
15257                 this.el.remove();
15258                 if(this.actionMode == "container"){
15259                     this.container.remove();
15260                 }
15261             }
15262             this.onDestroy();
15263             Roo.ComponentMgr.unregister(this);
15264             this.fireEvent("destroy", this);
15265         }
15266     },
15267
15268         /** @private */
15269     beforeDestroy : function(){
15270
15271     },
15272
15273         /** @private */
15274         onDestroy : function(){
15275
15276     },
15277
15278     /**
15279      * Returns the underlying {@link Roo.Element}.
15280      * @return {Roo.Element} The element
15281      */
15282     getEl : function(){
15283         return this.el;
15284     },
15285
15286     /**
15287      * Returns the id of this component.
15288      * @return {String}
15289      */
15290     getId : function(){
15291         return this.id;
15292     },
15293
15294     /**
15295      * Try to focus this component.
15296      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15297      * @return {Roo.Component} this
15298      */
15299     focus : function(selectText){
15300         if(this.rendered){
15301             this.el.focus();
15302             if(selectText === true){
15303                 this.el.dom.select();
15304             }
15305         }
15306         return this;
15307     },
15308
15309     /** @private */
15310     blur : function(){
15311         if(this.rendered){
15312             this.el.blur();
15313         }
15314         return this;
15315     },
15316
15317     /**
15318      * Disable this component.
15319      * @return {Roo.Component} this
15320      */
15321     disable : function(){
15322         if(this.rendered){
15323             this.onDisable();
15324         }
15325         this.disabled = true;
15326         this.fireEvent("disable", this);
15327         return this;
15328     },
15329
15330         // private
15331     onDisable : function(){
15332         this.getActionEl().addClass(this.disabledClass);
15333         this.el.dom.disabled = true;
15334     },
15335
15336     /**
15337      * Enable this component.
15338      * @return {Roo.Component} this
15339      */
15340     enable : function(){
15341         if(this.rendered){
15342             this.onEnable();
15343         }
15344         this.disabled = false;
15345         this.fireEvent("enable", this);
15346         return this;
15347     },
15348
15349         // private
15350     onEnable : function(){
15351         this.getActionEl().removeClass(this.disabledClass);
15352         this.el.dom.disabled = false;
15353     },
15354
15355     /**
15356      * Convenience function for setting disabled/enabled by boolean.
15357      * @param {Boolean} disabled
15358      */
15359     setDisabled : function(disabled){
15360         this[disabled ? "disable" : "enable"]();
15361     },
15362
15363     /**
15364      * Show this component.
15365      * @return {Roo.Component} this
15366      */
15367     show: function(){
15368         if(this.fireEvent("beforeshow", this) !== false){
15369             this.hidden = false;
15370             if(this.rendered){
15371                 this.onShow();
15372             }
15373             this.fireEvent("show", this);
15374         }
15375         return this;
15376     },
15377
15378     // private
15379     onShow : function(){
15380         var ae = this.getActionEl();
15381         if(this.hideMode == 'visibility'){
15382             ae.dom.style.visibility = "visible";
15383         }else if(this.hideMode == 'offsets'){
15384             ae.removeClass('x-hidden');
15385         }else{
15386             ae.dom.style.display = "";
15387         }
15388     },
15389
15390     /**
15391      * Hide this component.
15392      * @return {Roo.Component} this
15393      */
15394     hide: function(){
15395         if(this.fireEvent("beforehide", this) !== false){
15396             this.hidden = true;
15397             if(this.rendered){
15398                 this.onHide();
15399             }
15400             this.fireEvent("hide", this);
15401         }
15402         return this;
15403     },
15404
15405     // private
15406     onHide : function(){
15407         var ae = this.getActionEl();
15408         if(this.hideMode == 'visibility'){
15409             ae.dom.style.visibility = "hidden";
15410         }else if(this.hideMode == 'offsets'){
15411             ae.addClass('x-hidden');
15412         }else{
15413             ae.dom.style.display = "none";
15414         }
15415     },
15416
15417     /**
15418      * Convenience function to hide or show this component by boolean.
15419      * @param {Boolean} visible True to show, false to hide
15420      * @return {Roo.Component} this
15421      */
15422     setVisible: function(visible){
15423         if(visible) {
15424             this.show();
15425         }else{
15426             this.hide();
15427         }
15428         return this;
15429     },
15430
15431     /**
15432      * Returns true if this component is visible.
15433      */
15434     isVisible : function(){
15435         return this.getActionEl().isVisible();
15436     },
15437
15438     cloneConfig : function(overrides){
15439         overrides = overrides || {};
15440         var id = overrides.id || Roo.id();
15441         var cfg = Roo.applyIf(overrides, this.initialConfig);
15442         cfg.id = id; // prevent dup id
15443         return new this.constructor(cfg);
15444     }
15445 });/*
15446  * Based on:
15447  * Ext JS Library 1.1.1
15448  * Copyright(c) 2006-2007, Ext JS, LLC.
15449  *
15450  * Originally Released Under LGPL - original licence link has changed is not relivant.
15451  *
15452  * Fork - LGPL
15453  * <script type="text/javascript">
15454  */
15455
15456 /**
15457  * @class Roo.BoxComponent
15458  * @extends Roo.Component
15459  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15460  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15461  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15462  * layout containers.
15463  * @constructor
15464  * @param {Roo.Element/String/Object} config The configuration options.
15465  */
15466 Roo.BoxComponent = function(config){
15467     Roo.Component.call(this, config);
15468     this.addEvents({
15469         /**
15470          * @event resize
15471          * Fires after the component is resized.
15472              * @param {Roo.Component} this
15473              * @param {Number} adjWidth The box-adjusted width that was set
15474              * @param {Number} adjHeight The box-adjusted height that was set
15475              * @param {Number} rawWidth The width that was originally specified
15476              * @param {Number} rawHeight The height that was originally specified
15477              */
15478         resize : true,
15479         /**
15480          * @event move
15481          * Fires after the component is moved.
15482              * @param {Roo.Component} this
15483              * @param {Number} x The new x position
15484              * @param {Number} y The new y position
15485              */
15486         move : true
15487     });
15488 };
15489
15490 Roo.extend(Roo.BoxComponent, Roo.Component, {
15491     // private, set in afterRender to signify that the component has been rendered
15492     boxReady : false,
15493     // private, used to defer height settings to subclasses
15494     deferHeight: false,
15495     /** @cfg {Number} width
15496      * width (optional) size of component
15497      */
15498      /** @cfg {Number} height
15499      * height (optional) size of component
15500      */
15501      
15502     /**
15503      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15504      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15505      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15506      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15507      * @return {Roo.BoxComponent} this
15508      */
15509     setSize : function(w, h){
15510         // support for standard size objects
15511         if(typeof w == 'object'){
15512             h = w.height;
15513             w = w.width;
15514         }
15515         // not rendered
15516         if(!this.boxReady){
15517             this.width = w;
15518             this.height = h;
15519             return this;
15520         }
15521
15522         // prevent recalcs when not needed
15523         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15524             return this;
15525         }
15526         this.lastSize = {width: w, height: h};
15527
15528         var adj = this.adjustSize(w, h);
15529         var aw = adj.width, ah = adj.height;
15530         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15531             var rz = this.getResizeEl();
15532             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15533                 rz.setSize(aw, ah);
15534             }else if(!this.deferHeight && ah !== undefined){
15535                 rz.setHeight(ah);
15536             }else if(aw !== undefined){
15537                 rz.setWidth(aw);
15538             }
15539             this.onResize(aw, ah, w, h);
15540             this.fireEvent('resize', this, aw, ah, w, h);
15541         }
15542         return this;
15543     },
15544
15545     /**
15546      * Gets the current size of the component's underlying element.
15547      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15548      */
15549     getSize : function(){
15550         return this.el.getSize();
15551     },
15552
15553     /**
15554      * Gets the current XY position of the component's underlying element.
15555      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15556      * @return {Array} The XY position of the element (e.g., [100, 200])
15557      */
15558     getPosition : function(local){
15559         if(local === true){
15560             return [this.el.getLeft(true), this.el.getTop(true)];
15561         }
15562         return this.xy || this.el.getXY();
15563     },
15564
15565     /**
15566      * Gets the current box measurements of the component's underlying element.
15567      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15568      * @returns {Object} box An object in the format {x, y, width, height}
15569      */
15570     getBox : function(local){
15571         var s = this.el.getSize();
15572         if(local){
15573             s.x = this.el.getLeft(true);
15574             s.y = this.el.getTop(true);
15575         }else{
15576             var xy = this.xy || this.el.getXY();
15577             s.x = xy[0];
15578             s.y = xy[1];
15579         }
15580         return s;
15581     },
15582
15583     /**
15584      * Sets the current box measurements of the component's underlying element.
15585      * @param {Object} box An object in the format {x, y, width, height}
15586      * @returns {Roo.BoxComponent} this
15587      */
15588     updateBox : function(box){
15589         this.setSize(box.width, box.height);
15590         this.setPagePosition(box.x, box.y);
15591         return this;
15592     },
15593
15594     // protected
15595     getResizeEl : function(){
15596         return this.resizeEl || this.el;
15597     },
15598
15599     // protected
15600     getPositionEl : function(){
15601         return this.positionEl || this.el;
15602     },
15603
15604     /**
15605      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15606      * This method fires the move event.
15607      * @param {Number} left The new left
15608      * @param {Number} top The new top
15609      * @returns {Roo.BoxComponent} this
15610      */
15611     setPosition : function(x, y){
15612         this.x = x;
15613         this.y = y;
15614         if(!this.boxReady){
15615             return this;
15616         }
15617         var adj = this.adjustPosition(x, y);
15618         var ax = adj.x, ay = adj.y;
15619
15620         var el = this.getPositionEl();
15621         if(ax !== undefined || ay !== undefined){
15622             if(ax !== undefined && ay !== undefined){
15623                 el.setLeftTop(ax, ay);
15624             }else if(ax !== undefined){
15625                 el.setLeft(ax);
15626             }else if(ay !== undefined){
15627                 el.setTop(ay);
15628             }
15629             this.onPosition(ax, ay);
15630             this.fireEvent('move', this, ax, ay);
15631         }
15632         return this;
15633     },
15634
15635     /**
15636      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15637      * This method fires the move event.
15638      * @param {Number} x The new x position
15639      * @param {Number} y The new y position
15640      * @returns {Roo.BoxComponent} this
15641      */
15642     setPagePosition : function(x, y){
15643         this.pageX = x;
15644         this.pageY = y;
15645         if(!this.boxReady){
15646             return;
15647         }
15648         if(x === undefined || y === undefined){ // cannot translate undefined points
15649             return;
15650         }
15651         var p = this.el.translatePoints(x, y);
15652         this.setPosition(p.left, p.top);
15653         return this;
15654     },
15655
15656     // private
15657     onRender : function(ct, position){
15658         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15659         if(this.resizeEl){
15660             this.resizeEl = Roo.get(this.resizeEl);
15661         }
15662         if(this.positionEl){
15663             this.positionEl = Roo.get(this.positionEl);
15664         }
15665     },
15666
15667     // private
15668     afterRender : function(){
15669         Roo.BoxComponent.superclass.afterRender.call(this);
15670         this.boxReady = true;
15671         this.setSize(this.width, this.height);
15672         if(this.x || this.y){
15673             this.setPosition(this.x, this.y);
15674         }
15675         if(this.pageX || this.pageY){
15676             this.setPagePosition(this.pageX, this.pageY);
15677         }
15678     },
15679
15680     /**
15681      * Force the component's size to recalculate based on the underlying element's current height and width.
15682      * @returns {Roo.BoxComponent} this
15683      */
15684     syncSize : function(){
15685         delete this.lastSize;
15686         this.setSize(this.el.getWidth(), this.el.getHeight());
15687         return this;
15688     },
15689
15690     /**
15691      * Called after the component is resized, this method is empty by default but can be implemented by any
15692      * subclass that needs to perform custom logic after a resize occurs.
15693      * @param {Number} adjWidth The box-adjusted width that was set
15694      * @param {Number} adjHeight The box-adjusted height that was set
15695      * @param {Number} rawWidth The width that was originally specified
15696      * @param {Number} rawHeight The height that was originally specified
15697      */
15698     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15699
15700     },
15701
15702     /**
15703      * Called after the component is moved, this method is empty by default but can be implemented by any
15704      * subclass that needs to perform custom logic after a move occurs.
15705      * @param {Number} x The new x position
15706      * @param {Number} y The new y position
15707      */
15708     onPosition : function(x, y){
15709
15710     },
15711
15712     // private
15713     adjustSize : function(w, h){
15714         if(this.autoWidth){
15715             w = 'auto';
15716         }
15717         if(this.autoHeight){
15718             h = 'auto';
15719         }
15720         return {width : w, height: h};
15721     },
15722
15723     // private
15724     adjustPosition : function(x, y){
15725         return {x : x, y: y};
15726     }
15727 });/*
15728  * Original code for Roojs - LGPL
15729  * <script type="text/javascript">
15730  */
15731  
15732 /**
15733  * @class Roo.XComponent
15734  * A delayed Element creator...
15735  * Or a way to group chunks of interface together.
15736  * 
15737  * Mypart.xyx = new Roo.XComponent({
15738
15739     parent : 'Mypart.xyz', // empty == document.element.!!
15740     order : '001',
15741     name : 'xxxx'
15742     region : 'xxxx'
15743     disabled : function() {} 
15744      
15745     tree : function() { // return an tree of xtype declared components
15746         var MODULE = this;
15747         return 
15748         {
15749             xtype : 'NestedLayoutPanel',
15750             // technicall
15751         }
15752      ]
15753  *})
15754  *
15755  *
15756  * It can be used to build a big heiracy, with parent etc.
15757  * or you can just use this to render a single compoent to a dom element
15758  * MYPART.render(Roo.Element | String(id) | dom_element )
15759  * 
15760  * @extends Roo.util.Observable
15761  * @constructor
15762  * @param cfg {Object} configuration of component
15763  * 
15764  */
15765 Roo.XComponent = function(cfg) {
15766     Roo.apply(this, cfg);
15767     this.addEvents({ 
15768         /**
15769              * @event built
15770              * Fires when this the componnt is built
15771              * @param {Roo.XComponent} c the component
15772              */
15773         'built' : true
15774         
15775     });
15776     this.region = this.region || 'center'; // default..
15777     Roo.XComponent.register(this);
15778     this.modules = false;
15779     this.el = false; // where the layout goes..
15780     
15781     
15782 }
15783 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15784     /**
15785      * @property el
15786      * The created element (with Roo.factory())
15787      * @type {Roo.Layout}
15788      */
15789     el  : false,
15790     
15791     /**
15792      * @property el
15793      * for BC  - use el in new code
15794      * @type {Roo.Layout}
15795      */
15796     panel : false,
15797     
15798     /**
15799      * @property layout
15800      * for BC  - use el in new code
15801      * @type {Roo.Layout}
15802      */
15803     layout : false,
15804     
15805      /**
15806      * @cfg {Function|boolean} disabled
15807      * If this module is disabled by some rule, return true from the funtion
15808      */
15809     disabled : false,
15810     
15811     /**
15812      * @cfg {String} parent 
15813      * Name of parent element which it get xtype added to..
15814      */
15815     parent: false,
15816     
15817     /**
15818      * @cfg {String} order
15819      * Used to set the order in which elements are created (usefull for multiple tabs)
15820      */
15821     
15822     order : false,
15823     /**
15824      * @cfg {String} name
15825      * String to display while loading.
15826      */
15827     name : false,
15828     /**
15829      * @cfg {String} region
15830      * Region to render component to (defaults to center)
15831      */
15832     region : 'center',
15833     
15834     /**
15835      * @cfg {Array} items
15836      * A single item array - the first element is the root of the tree..
15837      * It's done this way to stay compatible with the Xtype system...
15838      */
15839     items : false,
15840     
15841     /**
15842      * @property _tree
15843      * The method that retuns the tree of parts that make up this compoennt 
15844      * @type {function}
15845      */
15846     _tree  : false,
15847     
15848      /**
15849      * render
15850      * render element to dom or tree
15851      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15852      */
15853     
15854     render : function(el)
15855     {
15856         
15857         el = el || false;
15858         var hp = this.parent ? 1 : 0;
15859         
15860         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15861             // if parent is a '#.....' string, then let's use that..
15862             var ename = this.parent.substr(1)
15863             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15864             el = Roo.get(ename);
15865             if (!el && !this.parent) {
15866                 Roo.log("Warning - element can not be found :#" + ename );
15867                 return;
15868             }
15869         }
15870         var tree = this._tree ? this._tree() : this.tree();
15871
15872         // altertive root elements ??? - we need a better way to indicate these.
15873         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15874                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15875         
15876         if (!this.parent && is_alt) {
15877             //el = Roo.get(document.body);
15878             this.parent = { el : true };
15879         }
15880             
15881             
15882         
15883         if (!this.parent) {
15884             
15885             Roo.log("no parent - creating one");
15886             
15887             el = el ? Roo.get(el) : false;      
15888             
15889             // it's a top level one..
15890             this.parent =  {
15891                 el : new Roo.BorderLayout(el || document.body, {
15892                 
15893                      center: {
15894                          titlebar: false,
15895                          autoScroll:false,
15896                          closeOnTab: true,
15897                          tabPosition: 'top',
15898                           //resizeTabs: true,
15899                          alwaysShowTabs: el && hp? false :  true,
15900                          hideTabs: el || !hp ? true :  false,
15901                          minTabWidth: 140
15902                      }
15903                  })
15904             }
15905         }
15906         
15907                 if (!this.parent.el) {
15908                         // probably an old style ctor, which has been disabled.
15909                         return;
15910                         
15911                 }
15912                 // The 'tree' method is  '_tree now' 
15913             
15914         tree.region = tree.region || this.region;
15915         
15916         if (this.parent.el === true) {
15917             // bootstrap... - body..
15918             this.parent.el = Roo.factory(tree);
15919         }
15920         
15921         this.el = this.parent.el.addxtype(tree);
15922         this.fireEvent('built', this);
15923         
15924         this.panel = this.el;
15925         this.layout = this.panel.layout;
15926                 this.parentLayout = this.parent.layout  || false;  
15927          
15928     }
15929     
15930 });
15931
15932 Roo.apply(Roo.XComponent, {
15933     /**
15934      * @property  hideProgress
15935      * true to disable the building progress bar.. usefull on single page renders.
15936      * @type Boolean
15937      */
15938     hideProgress : false,
15939     /**
15940      * @property  buildCompleted
15941      * True when the builder has completed building the interface.
15942      * @type Boolean
15943      */
15944     buildCompleted : false,
15945      
15946     /**
15947      * @property  topModule
15948      * the upper most module - uses document.element as it's constructor.
15949      * @type Object
15950      */
15951      
15952     topModule  : false,
15953       
15954     /**
15955      * @property  modules
15956      * array of modules to be created by registration system.
15957      * @type {Array} of Roo.XComponent
15958      */
15959     
15960     modules : [],
15961     /**
15962      * @property  elmodules
15963      * array of modules to be created by which use #ID 
15964      * @type {Array} of Roo.XComponent
15965      */
15966      
15967     elmodules : [],
15968
15969      /**
15970      * @property  build_from_html
15971      * Build elements from html - used by bootstrap HTML stuff 
15972      *    - this is cleared after build is completed
15973      * @type {boolean} true  (default false)
15974      */
15975      
15976     build_from_html : false,
15977
15978     /**
15979      * Register components to be built later.
15980      *
15981      * This solves the following issues
15982      * - Building is not done on page load, but after an authentication process has occured.
15983      * - Interface elements are registered on page load
15984      * - Parent Interface elements may not be loaded before child, so this handles that..
15985      * 
15986      *
15987      * example:
15988      * 
15989      * MyApp.register({
15990           order : '000001',
15991           module : 'Pman.Tab.projectMgr',
15992           region : 'center',
15993           parent : 'Pman.layout',
15994           disabled : false,  // or use a function..
15995         })
15996      
15997      * * @param {Object} details about module
15998      */
15999     register : function(obj) {
16000                 
16001         Roo.XComponent.event.fireEvent('register', obj);
16002         switch(typeof(obj.disabled) ) {
16003                 
16004             case 'undefined':
16005                 break;
16006             
16007             case 'function':
16008                 if ( obj.disabled() ) {
16009                         return;
16010                 }
16011                 break;
16012             
16013             default:
16014                 if (obj.disabled) {
16015                         return;
16016                 }
16017                 break;
16018         }
16019                 
16020         this.modules.push(obj);
16021          
16022     },
16023     /**
16024      * convert a string to an object..
16025      * eg. 'AAA.BBB' -> finds AAA.BBB
16026
16027      */
16028     
16029     toObject : function(str)
16030     {
16031         if (!str || typeof(str) == 'object') {
16032             return str;
16033         }
16034         if (str.substring(0,1) == '#') {
16035             return str;
16036         }
16037
16038         var ar = str.split('.');
16039         var rt, o;
16040         rt = ar.shift();
16041             /** eval:var:o */
16042         try {
16043             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16044         } catch (e) {
16045             throw "Module not found : " + str;
16046         }
16047         
16048         if (o === false) {
16049             throw "Module not found : " + str;
16050         }
16051         Roo.each(ar, function(e) {
16052             if (typeof(o[e]) == 'undefined') {
16053                 throw "Module not found : " + str;
16054             }
16055             o = o[e];
16056         });
16057         
16058         return o;
16059         
16060     },
16061     
16062     
16063     /**
16064      * move modules into their correct place in the tree..
16065      * 
16066      */
16067     preBuild : function ()
16068     {
16069         var _t = this;
16070         Roo.each(this.modules , function (obj)
16071         {
16072             Roo.XComponent.event.fireEvent('beforebuild', obj);
16073             
16074             var opar = obj.parent;
16075             try { 
16076                 obj.parent = this.toObject(opar);
16077             } catch(e) {
16078                 Roo.log("parent:toObject failed: " + e.toString());
16079                 return;
16080             }
16081             
16082             if (!obj.parent) {
16083                 Roo.debug && Roo.log("GOT top level module");
16084                 Roo.debug && Roo.log(obj);
16085                 obj.modules = new Roo.util.MixedCollection(false, 
16086                     function(o) { return o.order + '' }
16087                 );
16088                 this.topModule = obj;
16089                 return;
16090             }
16091                         // parent is a string (usually a dom element name..)
16092             if (typeof(obj.parent) == 'string') {
16093                 this.elmodules.push(obj);
16094                 return;
16095             }
16096             if (obj.parent.constructor != Roo.XComponent) {
16097                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16098             }
16099             if (!obj.parent.modules) {
16100                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16101                     function(o) { return o.order + '' }
16102                 );
16103             }
16104             if (obj.parent.disabled) {
16105                 obj.disabled = true;
16106             }
16107             obj.parent.modules.add(obj);
16108         }, this);
16109     },
16110     
16111      /**
16112      * make a list of modules to build.
16113      * @return {Array} list of modules. 
16114      */ 
16115     
16116     buildOrder : function()
16117     {
16118         var _this = this;
16119         var cmp = function(a,b) {   
16120             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16121         };
16122         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16123             throw "No top level modules to build";
16124         }
16125         
16126         // make a flat list in order of modules to build.
16127         var mods = this.topModule ? [ this.topModule ] : [];
16128                 
16129         
16130         // elmodules (is a list of DOM based modules )
16131         Roo.each(this.elmodules, function(e) {
16132             mods.push(e);
16133             if (!this.topModule &&
16134                 typeof(e.parent) == 'string' &&
16135                 e.parent.substring(0,1) == '#' &&
16136                 Roo.get(e.parent.substr(1))
16137                ) {
16138                 
16139                 _this.topModule = e;
16140             }
16141             
16142         });
16143
16144         
16145         // add modules to their parents..
16146         var addMod = function(m) {
16147             Roo.debug && Roo.log("build Order: add: " + m.name);
16148                 
16149             mods.push(m);
16150             if (m.modules && !m.disabled) {
16151                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16152                 m.modules.keySort('ASC',  cmp );
16153                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16154     
16155                 m.modules.each(addMod);
16156             } else {
16157                 Roo.debug && Roo.log("build Order: no child modules");
16158             }
16159             // not sure if this is used any more..
16160             if (m.finalize) {
16161                 m.finalize.name = m.name + " (clean up) ";
16162                 mods.push(m.finalize);
16163             }
16164             
16165         }
16166         if (this.topModule && this.topModule.modules) { 
16167             this.topModule.modules.keySort('ASC',  cmp );
16168             this.topModule.modules.each(addMod);
16169         } 
16170         return mods;
16171     },
16172     
16173      /**
16174      * Build the registered modules.
16175      * @param {Object} parent element.
16176      * @param {Function} optional method to call after module has been added.
16177      * 
16178      */ 
16179    
16180     build : function(opts) 
16181     {
16182         
16183         if (typeof(opts) != 'undefined') {
16184             Roo.apply(this,opts);
16185         }
16186         
16187         this.preBuild();
16188         var mods = this.buildOrder();
16189       
16190         //this.allmods = mods;
16191         //Roo.debug && Roo.log(mods);
16192         //return;
16193         if (!mods.length) { // should not happen
16194             throw "NO modules!!!";
16195         }
16196         
16197         
16198         var msg = "Building Interface...";
16199         // flash it up as modal - so we store the mask!?
16200         if (!this.hideProgress && Roo.MessageBox) {
16201             Roo.MessageBox.show({ title: 'loading' });
16202             Roo.MessageBox.show({
16203                title: "Please wait...",
16204                msg: msg,
16205                width:450,
16206                progress:true,
16207                closable:false,
16208                modal: false
16209               
16210             });
16211         }
16212         var total = mods.length;
16213         
16214         var _this = this;
16215         var progressRun = function() {
16216             if (!mods.length) {
16217                 Roo.debug && Roo.log('hide?');
16218                 if (!this.hideProgress && Roo.MessageBox) {
16219                     Roo.MessageBox.hide();
16220                 }
16221                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16222                 
16223                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16224                 
16225                 // THE END...
16226                 return false;   
16227             }
16228             
16229             var m = mods.shift();
16230             
16231             
16232             Roo.debug && Roo.log(m);
16233             // not sure if this is supported any more.. - modules that are are just function
16234             if (typeof(m) == 'function') { 
16235                 m.call(this);
16236                 return progressRun.defer(10, _this);
16237             } 
16238             
16239             
16240             msg = "Building Interface " + (total  - mods.length) + 
16241                     " of " + total + 
16242                     (m.name ? (' - ' + m.name) : '');
16243                         Roo.debug && Roo.log(msg);
16244             if (!this.hideProgress &&  Roo.MessageBox) { 
16245                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16246             }
16247             
16248          
16249             // is the module disabled?
16250             var disabled = (typeof(m.disabled) == 'function') ?
16251                 m.disabled.call(m.module.disabled) : m.disabled;    
16252             
16253             
16254             if (disabled) {
16255                 return progressRun(); // we do not update the display!
16256             }
16257             
16258             // now build 
16259             
16260                         
16261                         
16262             m.render();
16263             // it's 10 on top level, and 1 on others??? why...
16264             return progressRun.defer(10, _this);
16265              
16266         }
16267         progressRun.defer(1, _this);
16268      
16269         
16270         
16271     },
16272         
16273         
16274         /**
16275          * Event Object.
16276          *
16277          *
16278          */
16279         event: false, 
16280     /**
16281          * wrapper for event.on - aliased later..  
16282          * Typically use to register a event handler for register:
16283          *
16284          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16285          *
16286          */
16287     on : false
16288    
16289     
16290     
16291 });
16292
16293 Roo.XComponent.event = new Roo.util.Observable({
16294                 events : { 
16295                         /**
16296                          * @event register
16297                          * Fires when an Component is registered,
16298                          * set the disable property on the Component to stop registration.
16299                          * @param {Roo.XComponent} c the component being registerd.
16300                          * 
16301                          */
16302                         'register' : true,
16303             /**
16304                          * @event beforebuild
16305                          * Fires before each Component is built
16306                          * can be used to apply permissions.
16307                          * @param {Roo.XComponent} c the component being registerd.
16308                          * 
16309                          */
16310                         'beforebuild' : true,
16311                         /**
16312                          * @event buildcomplete
16313                          * Fires on the top level element when all elements have been built
16314                          * @param {Roo.XComponent} the top level component.
16315                          */
16316                         'buildcomplete' : true
16317                         
16318                 }
16319 });
16320
16321 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16322  /*
16323  * Based on:
16324  * Ext JS Library 1.1.1
16325  * Copyright(c) 2006-2007, Ext JS, LLC.
16326  *
16327  * Originally Released Under LGPL - original licence link has changed is not relivant.
16328  *
16329  * Fork - LGPL
16330  * <script type="text/javascript">
16331  */
16332
16333
16334
16335 /*
16336  * These classes are derivatives of the similarly named classes in the YUI Library.
16337  * The original license:
16338  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16339  * Code licensed under the BSD License:
16340  * http://developer.yahoo.net/yui/license.txt
16341  */
16342
16343 (function() {
16344
16345 var Event=Roo.EventManager;
16346 var Dom=Roo.lib.Dom;
16347
16348 /**
16349  * @class Roo.dd.DragDrop
16350  * @extends Roo.util.Observable
16351  * Defines the interface and base operation of items that that can be
16352  * dragged or can be drop targets.  It was designed to be extended, overriding
16353  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16354  * Up to three html elements can be associated with a DragDrop instance:
16355  * <ul>
16356  * <li>linked element: the element that is passed into the constructor.
16357  * This is the element which defines the boundaries for interaction with
16358  * other DragDrop objects.</li>
16359  * <li>handle element(s): The drag operation only occurs if the element that
16360  * was clicked matches a handle element.  By default this is the linked
16361  * element, but there are times that you will want only a portion of the
16362  * linked element to initiate the drag operation, and the setHandleElId()
16363  * method provides a way to define this.</li>
16364  * <li>drag element: this represents the element that would be moved along
16365  * with the cursor during a drag operation.  By default, this is the linked
16366  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16367  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16368  * </li>
16369  * </ul>
16370  * This class should not be instantiated until the onload event to ensure that
16371  * the associated elements are available.
16372  * The following would define a DragDrop obj that would interact with any
16373  * other DragDrop obj in the "group1" group:
16374  * <pre>
16375  *  dd = new Roo.dd.DragDrop("div1", "group1");
16376  * </pre>
16377  * Since none of the event handlers have been implemented, nothing would
16378  * actually happen if you were to run the code above.  Normally you would
16379  * override this class or one of the default implementations, but you can
16380  * also override the methods you want on an instance of the class...
16381  * <pre>
16382  *  dd.onDragDrop = function(e, id) {
16383  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16384  *  }
16385  * </pre>
16386  * @constructor
16387  * @param {String} id of the element that is linked to this instance
16388  * @param {String} sGroup the group of related DragDrop objects
16389  * @param {object} config an object containing configurable attributes
16390  *                Valid properties for DragDrop:
16391  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16392  */
16393 Roo.dd.DragDrop = function(id, sGroup, config) {
16394     if (id) {
16395         this.init(id, sGroup, config);
16396     }
16397     
16398 };
16399
16400 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16401
16402     /**
16403      * The id of the element associated with this object.  This is what we
16404      * refer to as the "linked element" because the size and position of
16405      * this element is used to determine when the drag and drop objects have
16406      * interacted.
16407      * @property id
16408      * @type String
16409      */
16410     id: null,
16411
16412     /**
16413      * Configuration attributes passed into the constructor
16414      * @property config
16415      * @type object
16416      */
16417     config: null,
16418
16419     /**
16420      * The id of the element that will be dragged.  By default this is same
16421      * as the linked element , but could be changed to another element. Ex:
16422      * Roo.dd.DDProxy
16423      * @property dragElId
16424      * @type String
16425      * @private
16426      */
16427     dragElId: null,
16428
16429     /**
16430      * the id of the element that initiates the drag operation.  By default
16431      * this is the linked element, but could be changed to be a child of this
16432      * element.  This lets us do things like only starting the drag when the
16433      * header element within the linked html element is clicked.
16434      * @property handleElId
16435      * @type String
16436      * @private
16437      */
16438     handleElId: null,
16439
16440     /**
16441      * An associative array of HTML tags that will be ignored if clicked.
16442      * @property invalidHandleTypes
16443      * @type {string: string}
16444      */
16445     invalidHandleTypes: null,
16446
16447     /**
16448      * An associative array of ids for elements that will be ignored if clicked
16449      * @property invalidHandleIds
16450      * @type {string: string}
16451      */
16452     invalidHandleIds: null,
16453
16454     /**
16455      * An indexted array of css class names for elements that will be ignored
16456      * if clicked.
16457      * @property invalidHandleClasses
16458      * @type string[]
16459      */
16460     invalidHandleClasses: null,
16461
16462     /**
16463      * The linked element's absolute X position at the time the drag was
16464      * started
16465      * @property startPageX
16466      * @type int
16467      * @private
16468      */
16469     startPageX: 0,
16470
16471     /**
16472      * The linked element's absolute X position at the time the drag was
16473      * started
16474      * @property startPageY
16475      * @type int
16476      * @private
16477      */
16478     startPageY: 0,
16479
16480     /**
16481      * The group defines a logical collection of DragDrop objects that are
16482      * related.  Instances only get events when interacting with other
16483      * DragDrop object in the same group.  This lets us define multiple
16484      * groups using a single DragDrop subclass if we want.
16485      * @property groups
16486      * @type {string: string}
16487      */
16488     groups: null,
16489
16490     /**
16491      * Individual drag/drop instances can be locked.  This will prevent
16492      * onmousedown start drag.
16493      * @property locked
16494      * @type boolean
16495      * @private
16496      */
16497     locked: false,
16498
16499     /**
16500      * Lock this instance
16501      * @method lock
16502      */
16503     lock: function() { this.locked = true; },
16504
16505     /**
16506      * Unlock this instace
16507      * @method unlock
16508      */
16509     unlock: function() { this.locked = false; },
16510
16511     /**
16512      * By default, all insances can be a drop target.  This can be disabled by
16513      * setting isTarget to false.
16514      * @method isTarget
16515      * @type boolean
16516      */
16517     isTarget: true,
16518
16519     /**
16520      * The padding configured for this drag and drop object for calculating
16521      * the drop zone intersection with this object.
16522      * @method padding
16523      * @type int[]
16524      */
16525     padding: null,
16526
16527     /**
16528      * Cached reference to the linked element
16529      * @property _domRef
16530      * @private
16531      */
16532     _domRef: null,
16533
16534     /**
16535      * Internal typeof flag
16536      * @property __ygDragDrop
16537      * @private
16538      */
16539     __ygDragDrop: true,
16540
16541     /**
16542      * Set to true when horizontal contraints are applied
16543      * @property constrainX
16544      * @type boolean
16545      * @private
16546      */
16547     constrainX: false,
16548
16549     /**
16550      * Set to true when vertical contraints are applied
16551      * @property constrainY
16552      * @type boolean
16553      * @private
16554      */
16555     constrainY: false,
16556
16557     /**
16558      * The left constraint
16559      * @property minX
16560      * @type int
16561      * @private
16562      */
16563     minX: 0,
16564
16565     /**
16566      * The right constraint
16567      * @property maxX
16568      * @type int
16569      * @private
16570      */
16571     maxX: 0,
16572
16573     /**
16574      * The up constraint
16575      * @property minY
16576      * @type int
16577      * @type int
16578      * @private
16579      */
16580     minY: 0,
16581
16582     /**
16583      * The down constraint
16584      * @property maxY
16585      * @type int
16586      * @private
16587      */
16588     maxY: 0,
16589
16590     /**
16591      * Maintain offsets when we resetconstraints.  Set to true when you want
16592      * the position of the element relative to its parent to stay the same
16593      * when the page changes
16594      *
16595      * @property maintainOffset
16596      * @type boolean
16597      */
16598     maintainOffset: false,
16599
16600     /**
16601      * Array of pixel locations the element will snap to if we specified a
16602      * horizontal graduation/interval.  This array is generated automatically
16603      * when you define a tick interval.
16604      * @property xTicks
16605      * @type int[]
16606      */
16607     xTicks: null,
16608
16609     /**
16610      * Array of pixel locations the element will snap to if we specified a
16611      * vertical graduation/interval.  This array is generated automatically
16612      * when you define a tick interval.
16613      * @property yTicks
16614      * @type int[]
16615      */
16616     yTicks: null,
16617
16618     /**
16619      * By default the drag and drop instance will only respond to the primary
16620      * button click (left button for a right-handed mouse).  Set to true to
16621      * allow drag and drop to start with any mouse click that is propogated
16622      * by the browser
16623      * @property primaryButtonOnly
16624      * @type boolean
16625      */
16626     primaryButtonOnly: true,
16627
16628     /**
16629      * The availabe property is false until the linked dom element is accessible.
16630      * @property available
16631      * @type boolean
16632      */
16633     available: false,
16634
16635     /**
16636      * By default, drags can only be initiated if the mousedown occurs in the
16637      * region the linked element is.  This is done in part to work around a
16638      * bug in some browsers that mis-report the mousedown if the previous
16639      * mouseup happened outside of the window.  This property is set to true
16640      * if outer handles are defined.
16641      *
16642      * @property hasOuterHandles
16643      * @type boolean
16644      * @default false
16645      */
16646     hasOuterHandles: false,
16647
16648     /**
16649      * Code that executes immediately before the startDrag event
16650      * @method b4StartDrag
16651      * @private
16652      */
16653     b4StartDrag: function(x, y) { },
16654
16655     /**
16656      * Abstract method called after a drag/drop object is clicked
16657      * and the drag or mousedown time thresholds have beeen met.
16658      * @method startDrag
16659      * @param {int} X click location
16660      * @param {int} Y click location
16661      */
16662     startDrag: function(x, y) { /* override this */ },
16663
16664     /**
16665      * Code that executes immediately before the onDrag event
16666      * @method b4Drag
16667      * @private
16668      */
16669     b4Drag: function(e) { },
16670
16671     /**
16672      * Abstract method called during the onMouseMove event while dragging an
16673      * object.
16674      * @method onDrag
16675      * @param {Event} e the mousemove event
16676      */
16677     onDrag: function(e) { /* override this */ },
16678
16679     /**
16680      * Abstract method called when this element fist begins hovering over
16681      * another DragDrop obj
16682      * @method onDragEnter
16683      * @param {Event} e the mousemove event
16684      * @param {String|DragDrop[]} id In POINT mode, the element
16685      * id this is hovering over.  In INTERSECT mode, an array of one or more
16686      * dragdrop items being hovered over.
16687      */
16688     onDragEnter: function(e, id) { /* override this */ },
16689
16690     /**
16691      * Code that executes immediately before the onDragOver event
16692      * @method b4DragOver
16693      * @private
16694      */
16695     b4DragOver: function(e) { },
16696
16697     /**
16698      * Abstract method called when this element is hovering over another
16699      * DragDrop obj
16700      * @method onDragOver
16701      * @param {Event} e the mousemove event
16702      * @param {String|DragDrop[]} id In POINT mode, the element
16703      * id this is hovering over.  In INTERSECT mode, an array of dd items
16704      * being hovered over.
16705      */
16706     onDragOver: function(e, id) { /* override this */ },
16707
16708     /**
16709      * Code that executes immediately before the onDragOut event
16710      * @method b4DragOut
16711      * @private
16712      */
16713     b4DragOut: function(e) { },
16714
16715     /**
16716      * Abstract method called when we are no longer hovering over an element
16717      * @method onDragOut
16718      * @param {Event} e the mousemove event
16719      * @param {String|DragDrop[]} id In POINT mode, the element
16720      * id this was hovering over.  In INTERSECT mode, an array of dd items
16721      * that the mouse is no longer over.
16722      */
16723     onDragOut: function(e, id) { /* override this */ },
16724
16725     /**
16726      * Code that executes immediately before the onDragDrop event
16727      * @method b4DragDrop
16728      * @private
16729      */
16730     b4DragDrop: function(e) { },
16731
16732     /**
16733      * Abstract method called when this item is dropped on another DragDrop
16734      * obj
16735      * @method onDragDrop
16736      * @param {Event} e the mouseup event
16737      * @param {String|DragDrop[]} id In POINT mode, the element
16738      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16739      * was dropped on.
16740      */
16741     onDragDrop: function(e, id) { /* override this */ },
16742
16743     /**
16744      * Abstract method called when this item is dropped on an area with no
16745      * drop target
16746      * @method onInvalidDrop
16747      * @param {Event} e the mouseup event
16748      */
16749     onInvalidDrop: function(e) { /* override this */ },
16750
16751     /**
16752      * Code that executes immediately before the endDrag event
16753      * @method b4EndDrag
16754      * @private
16755      */
16756     b4EndDrag: function(e) { },
16757
16758     /**
16759      * Fired when we are done dragging the object
16760      * @method endDrag
16761      * @param {Event} e the mouseup event
16762      */
16763     endDrag: function(e) { /* override this */ },
16764
16765     /**
16766      * Code executed immediately before the onMouseDown event
16767      * @method b4MouseDown
16768      * @param {Event} e the mousedown event
16769      * @private
16770      */
16771     b4MouseDown: function(e) {  },
16772
16773     /**
16774      * Event handler that fires when a drag/drop obj gets a mousedown
16775      * @method onMouseDown
16776      * @param {Event} e the mousedown event
16777      */
16778     onMouseDown: function(e) { /* override this */ },
16779
16780     /**
16781      * Event handler that fires when a drag/drop obj gets a mouseup
16782      * @method onMouseUp
16783      * @param {Event} e the mouseup event
16784      */
16785     onMouseUp: function(e) { /* override this */ },
16786
16787     /**
16788      * Override the onAvailable method to do what is needed after the initial
16789      * position was determined.
16790      * @method onAvailable
16791      */
16792     onAvailable: function () {
16793     },
16794
16795     /*
16796      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16797      * @type Object
16798      */
16799     defaultPadding : {left:0, right:0, top:0, bottom:0},
16800
16801     /*
16802      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16803  *
16804  * Usage:
16805  <pre><code>
16806  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16807                 { dragElId: "existingProxyDiv" });
16808  dd.startDrag = function(){
16809      this.constrainTo("parent-id");
16810  };
16811  </code></pre>
16812  * Or you can initalize it using the {@link Roo.Element} object:
16813  <pre><code>
16814  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16815      startDrag : function(){
16816          this.constrainTo("parent-id");
16817      }
16818  });
16819  </code></pre>
16820      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16821      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16822      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16823      * an object containing the sides to pad. For example: {right:10, bottom:10}
16824      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16825      */
16826     constrainTo : function(constrainTo, pad, inContent){
16827         if(typeof pad == "number"){
16828             pad = {left: pad, right:pad, top:pad, bottom:pad};
16829         }
16830         pad = pad || this.defaultPadding;
16831         var b = Roo.get(this.getEl()).getBox();
16832         var ce = Roo.get(constrainTo);
16833         var s = ce.getScroll();
16834         var c, cd = ce.dom;
16835         if(cd == document.body){
16836             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16837         }else{
16838             xy = ce.getXY();
16839             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16840         }
16841
16842
16843         var topSpace = b.y - c.y;
16844         var leftSpace = b.x - c.x;
16845
16846         this.resetConstraints();
16847         this.setXConstraint(leftSpace - (pad.left||0), // left
16848                 c.width - leftSpace - b.width - (pad.right||0) //right
16849         );
16850         this.setYConstraint(topSpace - (pad.top||0), //top
16851                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16852         );
16853     },
16854
16855     /**
16856      * Returns a reference to the linked element
16857      * @method getEl
16858      * @return {HTMLElement} the html element
16859      */
16860     getEl: function() {
16861         if (!this._domRef) {
16862             this._domRef = Roo.getDom(this.id);
16863         }
16864
16865         return this._domRef;
16866     },
16867
16868     /**
16869      * Returns a reference to the actual element to drag.  By default this is
16870      * the same as the html element, but it can be assigned to another
16871      * element. An example of this can be found in Roo.dd.DDProxy
16872      * @method getDragEl
16873      * @return {HTMLElement} the html element
16874      */
16875     getDragEl: function() {
16876         return Roo.getDom(this.dragElId);
16877     },
16878
16879     /**
16880      * Sets up the DragDrop object.  Must be called in the constructor of any
16881      * Roo.dd.DragDrop subclass
16882      * @method init
16883      * @param id the id of the linked element
16884      * @param {String} sGroup the group of related items
16885      * @param {object} config configuration attributes
16886      */
16887     init: function(id, sGroup, config) {
16888         this.initTarget(id, sGroup, config);
16889         if (!Roo.isTouch) {
16890             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16891         }
16892         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16893         // Event.on(this.id, "selectstart", Event.preventDefault);
16894     },
16895
16896     /**
16897      * Initializes Targeting functionality only... the object does not
16898      * get a mousedown handler.
16899      * @method initTarget
16900      * @param id the id of the linked element
16901      * @param {String} sGroup the group of related items
16902      * @param {object} config configuration attributes
16903      */
16904     initTarget: function(id, sGroup, config) {
16905
16906         // configuration attributes
16907         this.config = config || {};
16908
16909         // create a local reference to the drag and drop manager
16910         this.DDM = Roo.dd.DDM;
16911         // initialize the groups array
16912         this.groups = {};
16913
16914         // assume that we have an element reference instead of an id if the
16915         // parameter is not a string
16916         if (typeof id !== "string") {
16917             id = Roo.id(id);
16918         }
16919
16920         // set the id
16921         this.id = id;
16922
16923         // add to an interaction group
16924         this.addToGroup((sGroup) ? sGroup : "default");
16925
16926         // We don't want to register this as the handle with the manager
16927         // so we just set the id rather than calling the setter.
16928         this.handleElId = id;
16929
16930         // the linked element is the element that gets dragged by default
16931         this.setDragElId(id);
16932
16933         // by default, clicked anchors will not start drag operations.
16934         this.invalidHandleTypes = { A: "A" };
16935         this.invalidHandleIds = {};
16936         this.invalidHandleClasses = [];
16937
16938         this.applyConfig();
16939
16940         this.handleOnAvailable();
16941     },
16942
16943     /**
16944      * Applies the configuration parameters that were passed into the constructor.
16945      * This is supposed to happen at each level through the inheritance chain.  So
16946      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16947      * DragDrop in order to get all of the parameters that are available in
16948      * each object.
16949      * @method applyConfig
16950      */
16951     applyConfig: function() {
16952
16953         // configurable properties:
16954         //    padding, isTarget, maintainOffset, primaryButtonOnly
16955         this.padding           = this.config.padding || [0, 0, 0, 0];
16956         this.isTarget          = (this.config.isTarget !== false);
16957         this.maintainOffset    = (this.config.maintainOffset);
16958         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16959
16960     },
16961
16962     /**
16963      * Executed when the linked element is available
16964      * @method handleOnAvailable
16965      * @private
16966      */
16967     handleOnAvailable: function() {
16968         this.available = true;
16969         this.resetConstraints();
16970         this.onAvailable();
16971     },
16972
16973      /**
16974      * Configures the padding for the target zone in px.  Effectively expands
16975      * (or reduces) the virtual object size for targeting calculations.
16976      * Supports css-style shorthand; if only one parameter is passed, all sides
16977      * will have that padding, and if only two are passed, the top and bottom
16978      * will have the first param, the left and right the second.
16979      * @method setPadding
16980      * @param {int} iTop    Top pad
16981      * @param {int} iRight  Right pad
16982      * @param {int} iBot    Bot pad
16983      * @param {int} iLeft   Left pad
16984      */
16985     setPadding: function(iTop, iRight, iBot, iLeft) {
16986         // this.padding = [iLeft, iRight, iTop, iBot];
16987         if (!iRight && 0 !== iRight) {
16988             this.padding = [iTop, iTop, iTop, iTop];
16989         } else if (!iBot && 0 !== iBot) {
16990             this.padding = [iTop, iRight, iTop, iRight];
16991         } else {
16992             this.padding = [iTop, iRight, iBot, iLeft];
16993         }
16994     },
16995
16996     /**
16997      * Stores the initial placement of the linked element.
16998      * @method setInitialPosition
16999      * @param {int} diffX   the X offset, default 0
17000      * @param {int} diffY   the Y offset, default 0
17001      */
17002     setInitPosition: function(diffX, diffY) {
17003         var el = this.getEl();
17004
17005         if (!this.DDM.verifyEl(el)) {
17006             return;
17007         }
17008
17009         var dx = diffX || 0;
17010         var dy = diffY || 0;
17011
17012         var p = Dom.getXY( el );
17013
17014         this.initPageX = p[0] - dx;
17015         this.initPageY = p[1] - dy;
17016
17017         this.lastPageX = p[0];
17018         this.lastPageY = p[1];
17019
17020
17021         this.setStartPosition(p);
17022     },
17023
17024     /**
17025      * Sets the start position of the element.  This is set when the obj
17026      * is initialized, the reset when a drag is started.
17027      * @method setStartPosition
17028      * @param pos current position (from previous lookup)
17029      * @private
17030      */
17031     setStartPosition: function(pos) {
17032         var p = pos || Dom.getXY( this.getEl() );
17033         this.deltaSetXY = null;
17034
17035         this.startPageX = p[0];
17036         this.startPageY = p[1];
17037     },
17038
17039     /**
17040      * Add this instance to a group of related drag/drop objects.  All
17041      * instances belong to at least one group, and can belong to as many
17042      * groups as needed.
17043      * @method addToGroup
17044      * @param sGroup {string} the name of the group
17045      */
17046     addToGroup: function(sGroup) {
17047         this.groups[sGroup] = true;
17048         this.DDM.regDragDrop(this, sGroup);
17049     },
17050
17051     /**
17052      * Remove's this instance from the supplied interaction group
17053      * @method removeFromGroup
17054      * @param {string}  sGroup  The group to drop
17055      */
17056     removeFromGroup: function(sGroup) {
17057         if (this.groups[sGroup]) {
17058             delete this.groups[sGroup];
17059         }
17060
17061         this.DDM.removeDDFromGroup(this, sGroup);
17062     },
17063
17064     /**
17065      * Allows you to specify that an element other than the linked element
17066      * will be moved with the cursor during a drag
17067      * @method setDragElId
17068      * @param id {string} the id of the element that will be used to initiate the drag
17069      */
17070     setDragElId: function(id) {
17071         this.dragElId = id;
17072     },
17073
17074     /**
17075      * Allows you to specify a child of the linked element that should be
17076      * used to initiate the drag operation.  An example of this would be if
17077      * you have a content div with text and links.  Clicking anywhere in the
17078      * content area would normally start the drag operation.  Use this method
17079      * to specify that an element inside of the content div is the element
17080      * that starts the drag operation.
17081      * @method setHandleElId
17082      * @param id {string} the id of the element that will be used to
17083      * initiate the drag.
17084      */
17085     setHandleElId: function(id) {
17086         if (typeof id !== "string") {
17087             id = Roo.id(id);
17088         }
17089         this.handleElId = id;
17090         this.DDM.regHandle(this.id, id);
17091     },
17092
17093     /**
17094      * Allows you to set an element outside of the linked element as a drag
17095      * handle
17096      * @method setOuterHandleElId
17097      * @param id the id of the element that will be used to initiate the drag
17098      */
17099     setOuterHandleElId: function(id) {
17100         if (typeof id !== "string") {
17101             id = Roo.id(id);
17102         }
17103         Event.on(id, "mousedown",
17104                 this.handleMouseDown, this);
17105         this.setHandleElId(id);
17106
17107         this.hasOuterHandles = true;
17108     },
17109
17110     /**
17111      * Remove all drag and drop hooks for this element
17112      * @method unreg
17113      */
17114     unreg: function() {
17115         Event.un(this.id, "mousedown",
17116                 this.handleMouseDown);
17117         Event.un(this.id, "touchstart",
17118                 this.handleMouseDown);
17119         this._domRef = null;
17120         this.DDM._remove(this);
17121     },
17122
17123     destroy : function(){
17124         this.unreg();
17125     },
17126
17127     /**
17128      * Returns true if this instance is locked, or the drag drop mgr is locked
17129      * (meaning that all drag/drop is disabled on the page.)
17130      * @method isLocked
17131      * @return {boolean} true if this obj or all drag/drop is locked, else
17132      * false
17133      */
17134     isLocked: function() {
17135         return (this.DDM.isLocked() || this.locked);
17136     },
17137
17138     /**
17139      * Fired when this object is clicked
17140      * @method handleMouseDown
17141      * @param {Event} e
17142      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17143      * @private
17144      */
17145     handleMouseDown: function(e, oDD){
17146      
17147         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17148             //Roo.log('not touch/ button !=0');
17149             return;
17150         }
17151         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17152             return; // double touch..
17153         }
17154         
17155
17156         if (this.isLocked()) {
17157             //Roo.log('locked');
17158             return;
17159         }
17160
17161         this.DDM.refreshCache(this.groups);
17162 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17163         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17164         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17165             //Roo.log('no outer handes or not over target');
17166                 // do nothing.
17167         } else {
17168 //            Roo.log('check validator');
17169             if (this.clickValidator(e)) {
17170 //                Roo.log('validate success');
17171                 // set the initial element position
17172                 this.setStartPosition();
17173
17174
17175                 this.b4MouseDown(e);
17176                 this.onMouseDown(e);
17177
17178                 this.DDM.handleMouseDown(e, this);
17179
17180                 this.DDM.stopEvent(e);
17181             } else {
17182
17183
17184             }
17185         }
17186     },
17187
17188     clickValidator: function(e) {
17189         var target = e.getTarget();
17190         return ( this.isValidHandleChild(target) &&
17191                     (this.id == this.handleElId ||
17192                         this.DDM.handleWasClicked(target, this.id)) );
17193     },
17194
17195     /**
17196      * Allows you to specify a tag name that should not start a drag operation
17197      * when clicked.  This is designed to facilitate embedding links within a
17198      * drag handle that do something other than start the drag.
17199      * @method addInvalidHandleType
17200      * @param {string} tagName the type of element to exclude
17201      */
17202     addInvalidHandleType: function(tagName) {
17203         var type = tagName.toUpperCase();
17204         this.invalidHandleTypes[type] = type;
17205     },
17206
17207     /**
17208      * Lets you to specify an element id for a child of a drag handle
17209      * that should not initiate a drag
17210      * @method addInvalidHandleId
17211      * @param {string} id the element id of the element you wish to ignore
17212      */
17213     addInvalidHandleId: function(id) {
17214         if (typeof id !== "string") {
17215             id = Roo.id(id);
17216         }
17217         this.invalidHandleIds[id] = id;
17218     },
17219
17220     /**
17221      * Lets you specify a css class of elements that will not initiate a drag
17222      * @method addInvalidHandleClass
17223      * @param {string} cssClass the class of the elements you wish to ignore
17224      */
17225     addInvalidHandleClass: function(cssClass) {
17226         this.invalidHandleClasses.push(cssClass);
17227     },
17228
17229     /**
17230      * Unsets an excluded tag name set by addInvalidHandleType
17231      * @method removeInvalidHandleType
17232      * @param {string} tagName the type of element to unexclude
17233      */
17234     removeInvalidHandleType: function(tagName) {
17235         var type = tagName.toUpperCase();
17236         // this.invalidHandleTypes[type] = null;
17237         delete this.invalidHandleTypes[type];
17238     },
17239
17240     /**
17241      * Unsets an invalid handle id
17242      * @method removeInvalidHandleId
17243      * @param {string} id the id of the element to re-enable
17244      */
17245     removeInvalidHandleId: function(id) {
17246         if (typeof id !== "string") {
17247             id = Roo.id(id);
17248         }
17249         delete this.invalidHandleIds[id];
17250     },
17251
17252     /**
17253      * Unsets an invalid css class
17254      * @method removeInvalidHandleClass
17255      * @param {string} cssClass the class of the element(s) you wish to
17256      * re-enable
17257      */
17258     removeInvalidHandleClass: function(cssClass) {
17259         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17260             if (this.invalidHandleClasses[i] == cssClass) {
17261                 delete this.invalidHandleClasses[i];
17262             }
17263         }
17264     },
17265
17266     /**
17267      * Checks the tag exclusion list to see if this click should be ignored
17268      * @method isValidHandleChild
17269      * @param {HTMLElement} node the HTMLElement to evaluate
17270      * @return {boolean} true if this is a valid tag type, false if not
17271      */
17272     isValidHandleChild: function(node) {
17273
17274         var valid = true;
17275         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17276         var nodeName;
17277         try {
17278             nodeName = node.nodeName.toUpperCase();
17279         } catch(e) {
17280             nodeName = node.nodeName;
17281         }
17282         valid = valid && !this.invalidHandleTypes[nodeName];
17283         valid = valid && !this.invalidHandleIds[node.id];
17284
17285         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17286             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17287         }
17288
17289
17290         return valid;
17291
17292     },
17293
17294     /**
17295      * Create the array of horizontal tick marks if an interval was specified
17296      * in setXConstraint().
17297      * @method setXTicks
17298      * @private
17299      */
17300     setXTicks: function(iStartX, iTickSize) {
17301         this.xTicks = [];
17302         this.xTickSize = iTickSize;
17303
17304         var tickMap = {};
17305
17306         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17307             if (!tickMap[i]) {
17308                 this.xTicks[this.xTicks.length] = i;
17309                 tickMap[i] = true;
17310             }
17311         }
17312
17313         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17314             if (!tickMap[i]) {
17315                 this.xTicks[this.xTicks.length] = i;
17316                 tickMap[i] = true;
17317             }
17318         }
17319
17320         this.xTicks.sort(this.DDM.numericSort) ;
17321     },
17322
17323     /**
17324      * Create the array of vertical tick marks if an interval was specified in
17325      * setYConstraint().
17326      * @method setYTicks
17327      * @private
17328      */
17329     setYTicks: function(iStartY, iTickSize) {
17330         this.yTicks = [];
17331         this.yTickSize = iTickSize;
17332
17333         var tickMap = {};
17334
17335         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17336             if (!tickMap[i]) {
17337                 this.yTicks[this.yTicks.length] = i;
17338                 tickMap[i] = true;
17339             }
17340         }
17341
17342         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17343             if (!tickMap[i]) {
17344                 this.yTicks[this.yTicks.length] = i;
17345                 tickMap[i] = true;
17346             }
17347         }
17348
17349         this.yTicks.sort(this.DDM.numericSort) ;
17350     },
17351
17352     /**
17353      * By default, the element can be dragged any place on the screen.  Use
17354      * this method to limit the horizontal travel of the element.  Pass in
17355      * 0,0 for the parameters if you want to lock the drag to the y axis.
17356      * @method setXConstraint
17357      * @param {int} iLeft the number of pixels the element can move to the left
17358      * @param {int} iRight the number of pixels the element can move to the
17359      * right
17360      * @param {int} iTickSize optional parameter for specifying that the
17361      * element
17362      * should move iTickSize pixels at a time.
17363      */
17364     setXConstraint: function(iLeft, iRight, iTickSize) {
17365         this.leftConstraint = iLeft;
17366         this.rightConstraint = iRight;
17367
17368         this.minX = this.initPageX - iLeft;
17369         this.maxX = this.initPageX + iRight;
17370         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17371
17372         this.constrainX = true;
17373     },
17374
17375     /**
17376      * Clears any constraints applied to this instance.  Also clears ticks
17377      * since they can't exist independent of a constraint at this time.
17378      * @method clearConstraints
17379      */
17380     clearConstraints: function() {
17381         this.constrainX = false;
17382         this.constrainY = false;
17383         this.clearTicks();
17384     },
17385
17386     /**
17387      * Clears any tick interval defined for this instance
17388      * @method clearTicks
17389      */
17390     clearTicks: function() {
17391         this.xTicks = null;
17392         this.yTicks = null;
17393         this.xTickSize = 0;
17394         this.yTickSize = 0;
17395     },
17396
17397     /**
17398      * By default, the element can be dragged any place on the screen.  Set
17399      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17400      * parameters if you want to lock the drag to the x axis.
17401      * @method setYConstraint
17402      * @param {int} iUp the number of pixels the element can move up
17403      * @param {int} iDown the number of pixels the element can move down
17404      * @param {int} iTickSize optional parameter for specifying that the
17405      * element should move iTickSize pixels at a time.
17406      */
17407     setYConstraint: function(iUp, iDown, iTickSize) {
17408         this.topConstraint = iUp;
17409         this.bottomConstraint = iDown;
17410
17411         this.minY = this.initPageY - iUp;
17412         this.maxY = this.initPageY + iDown;
17413         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17414
17415         this.constrainY = true;
17416
17417     },
17418
17419     /**
17420      * resetConstraints must be called if you manually reposition a dd element.
17421      * @method resetConstraints
17422      * @param {boolean} maintainOffset
17423      */
17424     resetConstraints: function() {
17425
17426
17427         // Maintain offsets if necessary
17428         if (this.initPageX || this.initPageX === 0) {
17429             // figure out how much this thing has moved
17430             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17431             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17432
17433             this.setInitPosition(dx, dy);
17434
17435         // This is the first time we have detected the element's position
17436         } else {
17437             this.setInitPosition();
17438         }
17439
17440         if (this.constrainX) {
17441             this.setXConstraint( this.leftConstraint,
17442                                  this.rightConstraint,
17443                                  this.xTickSize        );
17444         }
17445
17446         if (this.constrainY) {
17447             this.setYConstraint( this.topConstraint,
17448                                  this.bottomConstraint,
17449                                  this.yTickSize         );
17450         }
17451     },
17452
17453     /**
17454      * Normally the drag element is moved pixel by pixel, but we can specify
17455      * that it move a number of pixels at a time.  This method resolves the
17456      * location when we have it set up like this.
17457      * @method getTick
17458      * @param {int} val where we want to place the object
17459      * @param {int[]} tickArray sorted array of valid points
17460      * @return {int} the closest tick
17461      * @private
17462      */
17463     getTick: function(val, tickArray) {
17464
17465         if (!tickArray) {
17466             // If tick interval is not defined, it is effectively 1 pixel,
17467             // so we return the value passed to us.
17468             return val;
17469         } else if (tickArray[0] >= val) {
17470             // The value is lower than the first tick, so we return the first
17471             // tick.
17472             return tickArray[0];
17473         } else {
17474             for (var i=0, len=tickArray.length; i<len; ++i) {
17475                 var next = i + 1;
17476                 if (tickArray[next] && tickArray[next] >= val) {
17477                     var diff1 = val - tickArray[i];
17478                     var diff2 = tickArray[next] - val;
17479                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17480                 }
17481             }
17482
17483             // The value is larger than the last tick, so we return the last
17484             // tick.
17485             return tickArray[tickArray.length - 1];
17486         }
17487     },
17488
17489     /**
17490      * toString method
17491      * @method toString
17492      * @return {string} string representation of the dd obj
17493      */
17494     toString: function() {
17495         return ("DragDrop " + this.id);
17496     }
17497
17498 });
17499
17500 })();
17501 /*
17502  * Based on:
17503  * Ext JS Library 1.1.1
17504  * Copyright(c) 2006-2007, Ext JS, LLC.
17505  *
17506  * Originally Released Under LGPL - original licence link has changed is not relivant.
17507  *
17508  * Fork - LGPL
17509  * <script type="text/javascript">
17510  */
17511
17512
17513 /**
17514  * The drag and drop utility provides a framework for building drag and drop
17515  * applications.  In addition to enabling drag and drop for specific elements,
17516  * the drag and drop elements are tracked by the manager class, and the
17517  * interactions between the various elements are tracked during the drag and
17518  * the implementing code is notified about these important moments.
17519  */
17520
17521 // Only load the library once.  Rewriting the manager class would orphan
17522 // existing drag and drop instances.
17523 if (!Roo.dd.DragDropMgr) {
17524
17525 /**
17526  * @class Roo.dd.DragDropMgr
17527  * DragDropMgr is a singleton that tracks the element interaction for
17528  * all DragDrop items in the window.  Generally, you will not call
17529  * this class directly, but it does have helper methods that could
17530  * be useful in your DragDrop implementations.
17531  * @singleton
17532  */
17533 Roo.dd.DragDropMgr = function() {
17534
17535     var Event = Roo.EventManager;
17536
17537     return {
17538
17539         /**
17540          * Two dimensional Array of registered DragDrop objects.  The first
17541          * dimension is the DragDrop item group, the second the DragDrop
17542          * object.
17543          * @property ids
17544          * @type {string: string}
17545          * @private
17546          * @static
17547          */
17548         ids: {},
17549
17550         /**
17551          * Array of element ids defined as drag handles.  Used to determine
17552          * if the element that generated the mousedown event is actually the
17553          * handle and not the html element itself.
17554          * @property handleIds
17555          * @type {string: string}
17556          * @private
17557          * @static
17558          */
17559         handleIds: {},
17560
17561         /**
17562          * the DragDrop object that is currently being dragged
17563          * @property dragCurrent
17564          * @type DragDrop
17565          * @private
17566          * @static
17567          **/
17568         dragCurrent: null,
17569
17570         /**
17571          * the DragDrop object(s) that are being hovered over
17572          * @property dragOvers
17573          * @type Array
17574          * @private
17575          * @static
17576          */
17577         dragOvers: {},
17578
17579         /**
17580          * the X distance between the cursor and the object being dragged
17581          * @property deltaX
17582          * @type int
17583          * @private
17584          * @static
17585          */
17586         deltaX: 0,
17587
17588         /**
17589          * the Y distance between the cursor and the object being dragged
17590          * @property deltaY
17591          * @type int
17592          * @private
17593          * @static
17594          */
17595         deltaY: 0,
17596
17597         /**
17598          * Flag to determine if we should prevent the default behavior of the
17599          * events we define. By default this is true, but this can be set to
17600          * false if you need the default behavior (not recommended)
17601          * @property preventDefault
17602          * @type boolean
17603          * @static
17604          */
17605         preventDefault: true,
17606
17607         /**
17608          * Flag to determine if we should stop the propagation of the events
17609          * we generate. This is true by default but you may want to set it to
17610          * false if the html element contains other features that require the
17611          * mouse click.
17612          * @property stopPropagation
17613          * @type boolean
17614          * @static
17615          */
17616         stopPropagation: true,
17617
17618         /**
17619          * Internal flag that is set to true when drag and drop has been
17620          * intialized
17621          * @property initialized
17622          * @private
17623          * @static
17624          */
17625         initalized: false,
17626
17627         /**
17628          * All drag and drop can be disabled.
17629          * @property locked
17630          * @private
17631          * @static
17632          */
17633         locked: false,
17634
17635         /**
17636          * Called the first time an element is registered.
17637          * @method init
17638          * @private
17639          * @static
17640          */
17641         init: function() {
17642             this.initialized = true;
17643         },
17644
17645         /**
17646          * In point mode, drag and drop interaction is defined by the
17647          * location of the cursor during the drag/drop
17648          * @property POINT
17649          * @type int
17650          * @static
17651          */
17652         POINT: 0,
17653
17654         /**
17655          * In intersect mode, drag and drop interactio nis defined by the
17656          * overlap of two or more drag and drop objects.
17657          * @property INTERSECT
17658          * @type int
17659          * @static
17660          */
17661         INTERSECT: 1,
17662
17663         /**
17664          * The current drag and drop mode.  Default: POINT
17665          * @property mode
17666          * @type int
17667          * @static
17668          */
17669         mode: 0,
17670
17671         /**
17672          * Runs method on all drag and drop objects
17673          * @method _execOnAll
17674          * @private
17675          * @static
17676          */
17677         _execOnAll: function(sMethod, args) {
17678             for (var i in this.ids) {
17679                 for (var j in this.ids[i]) {
17680                     var oDD = this.ids[i][j];
17681                     if (! this.isTypeOfDD(oDD)) {
17682                         continue;
17683                     }
17684                     oDD[sMethod].apply(oDD, args);
17685                 }
17686             }
17687         },
17688
17689         /**
17690          * Drag and drop initialization.  Sets up the global event handlers
17691          * @method _onLoad
17692          * @private
17693          * @static
17694          */
17695         _onLoad: function() {
17696
17697             this.init();
17698
17699             if (!Roo.isTouch) {
17700                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17701                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17702             }
17703             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17704             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17705             
17706             Event.on(window,   "unload",    this._onUnload, this, true);
17707             Event.on(window,   "resize",    this._onResize, this, true);
17708             // Event.on(window,   "mouseout",    this._test);
17709
17710         },
17711
17712         /**
17713          * Reset constraints on all drag and drop objs
17714          * @method _onResize
17715          * @private
17716          * @static
17717          */
17718         _onResize: function(e) {
17719             this._execOnAll("resetConstraints", []);
17720         },
17721
17722         /**
17723          * Lock all drag and drop functionality
17724          * @method lock
17725          * @static
17726          */
17727         lock: function() { this.locked = true; },
17728
17729         /**
17730          * Unlock all drag and drop functionality
17731          * @method unlock
17732          * @static
17733          */
17734         unlock: function() { this.locked = false; },
17735
17736         /**
17737          * Is drag and drop locked?
17738          * @method isLocked
17739          * @return {boolean} True if drag and drop is locked, false otherwise.
17740          * @static
17741          */
17742         isLocked: function() { return this.locked; },
17743
17744         /**
17745          * Location cache that is set for all drag drop objects when a drag is
17746          * initiated, cleared when the drag is finished.
17747          * @property locationCache
17748          * @private
17749          * @static
17750          */
17751         locationCache: {},
17752
17753         /**
17754          * Set useCache to false if you want to force object the lookup of each
17755          * drag and drop linked element constantly during a drag.
17756          * @property useCache
17757          * @type boolean
17758          * @static
17759          */
17760         useCache: true,
17761
17762         /**
17763          * The number of pixels that the mouse needs to move after the
17764          * mousedown before the drag is initiated.  Default=3;
17765          * @property clickPixelThresh
17766          * @type int
17767          * @static
17768          */
17769         clickPixelThresh: 3,
17770
17771         /**
17772          * The number of milliseconds after the mousedown event to initiate the
17773          * drag if we don't get a mouseup event. Default=1000
17774          * @property clickTimeThresh
17775          * @type int
17776          * @static
17777          */
17778         clickTimeThresh: 350,
17779
17780         /**
17781          * Flag that indicates that either the drag pixel threshold or the
17782          * mousdown time threshold has been met
17783          * @property dragThreshMet
17784          * @type boolean
17785          * @private
17786          * @static
17787          */
17788         dragThreshMet: false,
17789
17790         /**
17791          * Timeout used for the click time threshold
17792          * @property clickTimeout
17793          * @type Object
17794          * @private
17795          * @static
17796          */
17797         clickTimeout: null,
17798
17799         /**
17800          * The X position of the mousedown event stored for later use when a
17801          * drag threshold is met.
17802          * @property startX
17803          * @type int
17804          * @private
17805          * @static
17806          */
17807         startX: 0,
17808
17809         /**
17810          * The Y position of the mousedown event stored for later use when a
17811          * drag threshold is met.
17812          * @property startY
17813          * @type int
17814          * @private
17815          * @static
17816          */
17817         startY: 0,
17818
17819         /**
17820          * Each DragDrop instance must be registered with the DragDropMgr.
17821          * This is executed in DragDrop.init()
17822          * @method regDragDrop
17823          * @param {DragDrop} oDD the DragDrop object to register
17824          * @param {String} sGroup the name of the group this element belongs to
17825          * @static
17826          */
17827         regDragDrop: function(oDD, sGroup) {
17828             if (!this.initialized) { this.init(); }
17829
17830             if (!this.ids[sGroup]) {
17831                 this.ids[sGroup] = {};
17832             }
17833             this.ids[sGroup][oDD.id] = oDD;
17834         },
17835
17836         /**
17837          * Removes the supplied dd instance from the supplied group. Executed
17838          * by DragDrop.removeFromGroup, so don't call this function directly.
17839          * @method removeDDFromGroup
17840          * @private
17841          * @static
17842          */
17843         removeDDFromGroup: function(oDD, sGroup) {
17844             if (!this.ids[sGroup]) {
17845                 this.ids[sGroup] = {};
17846             }
17847
17848             var obj = this.ids[sGroup];
17849             if (obj && obj[oDD.id]) {
17850                 delete obj[oDD.id];
17851             }
17852         },
17853
17854         /**
17855          * Unregisters a drag and drop item.  This is executed in
17856          * DragDrop.unreg, use that method instead of calling this directly.
17857          * @method _remove
17858          * @private
17859          * @static
17860          */
17861         _remove: function(oDD) {
17862             for (var g in oDD.groups) {
17863                 if (g && this.ids[g][oDD.id]) {
17864                     delete this.ids[g][oDD.id];
17865                 }
17866             }
17867             delete this.handleIds[oDD.id];
17868         },
17869
17870         /**
17871          * Each DragDrop handle element must be registered.  This is done
17872          * automatically when executing DragDrop.setHandleElId()
17873          * @method regHandle
17874          * @param {String} sDDId the DragDrop id this element is a handle for
17875          * @param {String} sHandleId the id of the element that is the drag
17876          * handle
17877          * @static
17878          */
17879         regHandle: function(sDDId, sHandleId) {
17880             if (!this.handleIds[sDDId]) {
17881                 this.handleIds[sDDId] = {};
17882             }
17883             this.handleIds[sDDId][sHandleId] = sHandleId;
17884         },
17885
17886         /**
17887          * Utility function to determine if a given element has been
17888          * registered as a drag drop item.
17889          * @method isDragDrop
17890          * @param {String} id the element id to check
17891          * @return {boolean} true if this element is a DragDrop item,
17892          * false otherwise
17893          * @static
17894          */
17895         isDragDrop: function(id) {
17896             return ( this.getDDById(id) ) ? true : false;
17897         },
17898
17899         /**
17900          * Returns the drag and drop instances that are in all groups the
17901          * passed in instance belongs to.
17902          * @method getRelated
17903          * @param {DragDrop} p_oDD the obj to get related data for
17904          * @param {boolean} bTargetsOnly if true, only return targetable objs
17905          * @return {DragDrop[]} the related instances
17906          * @static
17907          */
17908         getRelated: function(p_oDD, bTargetsOnly) {
17909             var oDDs = [];
17910             for (var i in p_oDD.groups) {
17911                 for (j in this.ids[i]) {
17912                     var dd = this.ids[i][j];
17913                     if (! this.isTypeOfDD(dd)) {
17914                         continue;
17915                     }
17916                     if (!bTargetsOnly || dd.isTarget) {
17917                         oDDs[oDDs.length] = dd;
17918                     }
17919                 }
17920             }
17921
17922             return oDDs;
17923         },
17924
17925         /**
17926          * Returns true if the specified dd target is a legal target for
17927          * the specifice drag obj
17928          * @method isLegalTarget
17929          * @param {DragDrop} the drag obj
17930          * @param {DragDrop} the target
17931          * @return {boolean} true if the target is a legal target for the
17932          * dd obj
17933          * @static
17934          */
17935         isLegalTarget: function (oDD, oTargetDD) {
17936             var targets = this.getRelated(oDD, true);
17937             for (var i=0, len=targets.length;i<len;++i) {
17938                 if (targets[i].id == oTargetDD.id) {
17939                     return true;
17940                 }
17941             }
17942
17943             return false;
17944         },
17945
17946         /**
17947          * My goal is to be able to transparently determine if an object is
17948          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17949          * returns "object", oDD.constructor.toString() always returns
17950          * "DragDrop" and not the name of the subclass.  So for now it just
17951          * evaluates a well-known variable in DragDrop.
17952          * @method isTypeOfDD
17953          * @param {Object} the object to evaluate
17954          * @return {boolean} true if typeof oDD = DragDrop
17955          * @static
17956          */
17957         isTypeOfDD: function (oDD) {
17958             return (oDD && oDD.__ygDragDrop);
17959         },
17960
17961         /**
17962          * Utility function to determine if a given element has been
17963          * registered as a drag drop handle for the given Drag Drop object.
17964          * @method isHandle
17965          * @param {String} id the element id to check
17966          * @return {boolean} true if this element is a DragDrop handle, false
17967          * otherwise
17968          * @static
17969          */
17970         isHandle: function(sDDId, sHandleId) {
17971             return ( this.handleIds[sDDId] &&
17972                             this.handleIds[sDDId][sHandleId] );
17973         },
17974
17975         /**
17976          * Returns the DragDrop instance for a given id
17977          * @method getDDById
17978          * @param {String} id the id of the DragDrop object
17979          * @return {DragDrop} the drag drop object, null if it is not found
17980          * @static
17981          */
17982         getDDById: function(id) {
17983             for (var i in this.ids) {
17984                 if (this.ids[i][id]) {
17985                     return this.ids[i][id];
17986                 }
17987             }
17988             return null;
17989         },
17990
17991         /**
17992          * Fired after a registered DragDrop object gets the mousedown event.
17993          * Sets up the events required to track the object being dragged
17994          * @method handleMouseDown
17995          * @param {Event} e the event
17996          * @param oDD the DragDrop object being dragged
17997          * @private
17998          * @static
17999          */
18000         handleMouseDown: function(e, oDD) {
18001             if(Roo.QuickTips){
18002                 Roo.QuickTips.disable();
18003             }
18004             this.currentTarget = e.getTarget();
18005
18006             this.dragCurrent = oDD;
18007
18008             var el = oDD.getEl();
18009
18010             // track start position
18011             this.startX = e.getPageX();
18012             this.startY = e.getPageY();
18013
18014             this.deltaX = this.startX - el.offsetLeft;
18015             this.deltaY = this.startY - el.offsetTop;
18016
18017             this.dragThreshMet = false;
18018
18019             this.clickTimeout = setTimeout(
18020                     function() {
18021                         var DDM = Roo.dd.DDM;
18022                         DDM.startDrag(DDM.startX, DDM.startY);
18023                     },
18024                     this.clickTimeThresh );
18025         },
18026
18027         /**
18028          * Fired when either the drag pixel threshol or the mousedown hold
18029          * time threshold has been met.
18030          * @method startDrag
18031          * @param x {int} the X position of the original mousedown
18032          * @param y {int} the Y position of the original mousedown
18033          * @static
18034          */
18035         startDrag: function(x, y) {
18036             clearTimeout(this.clickTimeout);
18037             if (this.dragCurrent) {
18038                 this.dragCurrent.b4StartDrag(x, y);
18039                 this.dragCurrent.startDrag(x, y);
18040             }
18041             this.dragThreshMet = true;
18042         },
18043
18044         /**
18045          * Internal function to handle the mouseup event.  Will be invoked
18046          * from the context of the document.
18047          * @method handleMouseUp
18048          * @param {Event} e the event
18049          * @private
18050          * @static
18051          */
18052         handleMouseUp: function(e) {
18053
18054             if(Roo.QuickTips){
18055                 Roo.QuickTips.enable();
18056             }
18057             if (! this.dragCurrent) {
18058                 return;
18059             }
18060
18061             clearTimeout(this.clickTimeout);
18062
18063             if (this.dragThreshMet) {
18064                 this.fireEvents(e, true);
18065             } else {
18066             }
18067
18068             this.stopDrag(e);
18069
18070             this.stopEvent(e);
18071         },
18072
18073         /**
18074          * Utility to stop event propagation and event default, if these
18075          * features are turned on.
18076          * @method stopEvent
18077          * @param {Event} e the event as returned by this.getEvent()
18078          * @static
18079          */
18080         stopEvent: function(e){
18081             if(this.stopPropagation) {
18082                 e.stopPropagation();
18083             }
18084
18085             if (this.preventDefault) {
18086                 e.preventDefault();
18087             }
18088         },
18089
18090         /**
18091          * Internal function to clean up event handlers after the drag
18092          * operation is complete
18093          * @method stopDrag
18094          * @param {Event} e the event
18095          * @private
18096          * @static
18097          */
18098         stopDrag: function(e) {
18099             // Fire the drag end event for the item that was dragged
18100             if (this.dragCurrent) {
18101                 if (this.dragThreshMet) {
18102                     this.dragCurrent.b4EndDrag(e);
18103                     this.dragCurrent.endDrag(e);
18104                 }
18105
18106                 this.dragCurrent.onMouseUp(e);
18107             }
18108
18109             this.dragCurrent = null;
18110             this.dragOvers = {};
18111         },
18112
18113         /**
18114          * Internal function to handle the mousemove event.  Will be invoked
18115          * from the context of the html element.
18116          *
18117          * @TODO figure out what we can do about mouse events lost when the
18118          * user drags objects beyond the window boundary.  Currently we can
18119          * detect this in internet explorer by verifying that the mouse is
18120          * down during the mousemove event.  Firefox doesn't give us the
18121          * button state on the mousemove event.
18122          * @method handleMouseMove
18123          * @param {Event} e the event
18124          * @private
18125          * @static
18126          */
18127         handleMouseMove: function(e) {
18128             if (! this.dragCurrent) {
18129                 return true;
18130             }
18131
18132             // var button = e.which || e.button;
18133
18134             // check for IE mouseup outside of page boundary
18135             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18136                 this.stopEvent(e);
18137                 return this.handleMouseUp(e);
18138             }
18139
18140             if (!this.dragThreshMet) {
18141                 var diffX = Math.abs(this.startX - e.getPageX());
18142                 var diffY = Math.abs(this.startY - e.getPageY());
18143                 if (diffX > this.clickPixelThresh ||
18144                             diffY > this.clickPixelThresh) {
18145                     this.startDrag(this.startX, this.startY);
18146                 }
18147             }
18148
18149             if (this.dragThreshMet) {
18150                 this.dragCurrent.b4Drag(e);
18151                 this.dragCurrent.onDrag(e);
18152                 if(!this.dragCurrent.moveOnly){
18153                     this.fireEvents(e, false);
18154                 }
18155             }
18156
18157             this.stopEvent(e);
18158
18159             return true;
18160         },
18161
18162         /**
18163          * Iterates over all of the DragDrop elements to find ones we are
18164          * hovering over or dropping on
18165          * @method fireEvents
18166          * @param {Event} e the event
18167          * @param {boolean} isDrop is this a drop op or a mouseover op?
18168          * @private
18169          * @static
18170          */
18171         fireEvents: function(e, isDrop) {
18172             var dc = this.dragCurrent;
18173
18174             // If the user did the mouse up outside of the window, we could
18175             // get here even though we have ended the drag.
18176             if (!dc || dc.isLocked()) {
18177                 return;
18178             }
18179
18180             var pt = e.getPoint();
18181
18182             // cache the previous dragOver array
18183             var oldOvers = [];
18184
18185             var outEvts   = [];
18186             var overEvts  = [];
18187             var dropEvts  = [];
18188             var enterEvts = [];
18189
18190             // Check to see if the object(s) we were hovering over is no longer
18191             // being hovered over so we can fire the onDragOut event
18192             for (var i in this.dragOvers) {
18193
18194                 var ddo = this.dragOvers[i];
18195
18196                 if (! this.isTypeOfDD(ddo)) {
18197                     continue;
18198                 }
18199
18200                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18201                     outEvts.push( ddo );
18202                 }
18203
18204                 oldOvers[i] = true;
18205                 delete this.dragOvers[i];
18206             }
18207
18208             for (var sGroup in dc.groups) {
18209
18210                 if ("string" != typeof sGroup) {
18211                     continue;
18212                 }
18213
18214                 for (i in this.ids[sGroup]) {
18215                     var oDD = this.ids[sGroup][i];
18216                     if (! this.isTypeOfDD(oDD)) {
18217                         continue;
18218                     }
18219
18220                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18221                         if (this.isOverTarget(pt, oDD, this.mode)) {
18222                             // look for drop interactions
18223                             if (isDrop) {
18224                                 dropEvts.push( oDD );
18225                             // look for drag enter and drag over interactions
18226                             } else {
18227
18228                                 // initial drag over: dragEnter fires
18229                                 if (!oldOvers[oDD.id]) {
18230                                     enterEvts.push( oDD );
18231                                 // subsequent drag overs: dragOver fires
18232                                 } else {
18233                                     overEvts.push( oDD );
18234                                 }
18235
18236                                 this.dragOvers[oDD.id] = oDD;
18237                             }
18238                         }
18239                     }
18240                 }
18241             }
18242
18243             if (this.mode) {
18244                 if (outEvts.length) {
18245                     dc.b4DragOut(e, outEvts);
18246                     dc.onDragOut(e, outEvts);
18247                 }
18248
18249                 if (enterEvts.length) {
18250                     dc.onDragEnter(e, enterEvts);
18251                 }
18252
18253                 if (overEvts.length) {
18254                     dc.b4DragOver(e, overEvts);
18255                     dc.onDragOver(e, overEvts);
18256                 }
18257
18258                 if (dropEvts.length) {
18259                     dc.b4DragDrop(e, dropEvts);
18260                     dc.onDragDrop(e, dropEvts);
18261                 }
18262
18263             } else {
18264                 // fire dragout events
18265                 var len = 0;
18266                 for (i=0, len=outEvts.length; i<len; ++i) {
18267                     dc.b4DragOut(e, outEvts[i].id);
18268                     dc.onDragOut(e, outEvts[i].id);
18269                 }
18270
18271                 // fire enter events
18272                 for (i=0,len=enterEvts.length; i<len; ++i) {
18273                     // dc.b4DragEnter(e, oDD.id);
18274                     dc.onDragEnter(e, enterEvts[i].id);
18275                 }
18276
18277                 // fire over events
18278                 for (i=0,len=overEvts.length; i<len; ++i) {
18279                     dc.b4DragOver(e, overEvts[i].id);
18280                     dc.onDragOver(e, overEvts[i].id);
18281                 }
18282
18283                 // fire drop events
18284                 for (i=0, len=dropEvts.length; i<len; ++i) {
18285                     dc.b4DragDrop(e, dropEvts[i].id);
18286                     dc.onDragDrop(e, dropEvts[i].id);
18287                 }
18288
18289             }
18290
18291             // notify about a drop that did not find a target
18292             if (isDrop && !dropEvts.length) {
18293                 dc.onInvalidDrop(e);
18294             }
18295
18296         },
18297
18298         /**
18299          * Helper function for getting the best match from the list of drag
18300          * and drop objects returned by the drag and drop events when we are
18301          * in INTERSECT mode.  It returns either the first object that the
18302          * cursor is over, or the object that has the greatest overlap with
18303          * the dragged element.
18304          * @method getBestMatch
18305          * @param  {DragDrop[]} dds The array of drag and drop objects
18306          * targeted
18307          * @return {DragDrop}       The best single match
18308          * @static
18309          */
18310         getBestMatch: function(dds) {
18311             var winner = null;
18312             // Return null if the input is not what we expect
18313             //if (!dds || !dds.length || dds.length == 0) {
18314                // winner = null;
18315             // If there is only one item, it wins
18316             //} else if (dds.length == 1) {
18317
18318             var len = dds.length;
18319
18320             if (len == 1) {
18321                 winner = dds[0];
18322             } else {
18323                 // Loop through the targeted items
18324                 for (var i=0; i<len; ++i) {
18325                     var dd = dds[i];
18326                     // If the cursor is over the object, it wins.  If the
18327                     // cursor is over multiple matches, the first one we come
18328                     // to wins.
18329                     if (dd.cursorIsOver) {
18330                         winner = dd;
18331                         break;
18332                     // Otherwise the object with the most overlap wins
18333                     } else {
18334                         if (!winner ||
18335                             winner.overlap.getArea() < dd.overlap.getArea()) {
18336                             winner = dd;
18337                         }
18338                     }
18339                 }
18340             }
18341
18342             return winner;
18343         },
18344
18345         /**
18346          * Refreshes the cache of the top-left and bottom-right points of the
18347          * drag and drop objects in the specified group(s).  This is in the
18348          * format that is stored in the drag and drop instance, so typical
18349          * usage is:
18350          * <code>
18351          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18352          * </code>
18353          * Alternatively:
18354          * <code>
18355          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18356          * </code>
18357          * @TODO this really should be an indexed array.  Alternatively this
18358          * method could accept both.
18359          * @method refreshCache
18360          * @param {Object} groups an associative array of groups to refresh
18361          * @static
18362          */
18363         refreshCache: function(groups) {
18364             for (var sGroup in groups) {
18365                 if ("string" != typeof sGroup) {
18366                     continue;
18367                 }
18368                 for (var i in this.ids[sGroup]) {
18369                     var oDD = this.ids[sGroup][i];
18370
18371                     if (this.isTypeOfDD(oDD)) {
18372                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18373                         var loc = this.getLocation(oDD);
18374                         if (loc) {
18375                             this.locationCache[oDD.id] = loc;
18376                         } else {
18377                             delete this.locationCache[oDD.id];
18378                             // this will unregister the drag and drop object if
18379                             // the element is not in a usable state
18380                             // oDD.unreg();
18381                         }
18382                     }
18383                 }
18384             }
18385         },
18386
18387         /**
18388          * This checks to make sure an element exists and is in the DOM.  The
18389          * main purpose is to handle cases where innerHTML is used to remove
18390          * drag and drop objects from the DOM.  IE provides an 'unspecified
18391          * error' when trying to access the offsetParent of such an element
18392          * @method verifyEl
18393          * @param {HTMLElement} el the element to check
18394          * @return {boolean} true if the element looks usable
18395          * @static
18396          */
18397         verifyEl: function(el) {
18398             if (el) {
18399                 var parent;
18400                 if(Roo.isIE){
18401                     try{
18402                         parent = el.offsetParent;
18403                     }catch(e){}
18404                 }else{
18405                     parent = el.offsetParent;
18406                 }
18407                 if (parent) {
18408                     return true;
18409                 }
18410             }
18411
18412             return false;
18413         },
18414
18415         /**
18416          * Returns a Region object containing the drag and drop element's position
18417          * and size, including the padding configured for it
18418          * @method getLocation
18419          * @param {DragDrop} oDD the drag and drop object to get the
18420          *                       location for
18421          * @return {Roo.lib.Region} a Region object representing the total area
18422          *                             the element occupies, including any padding
18423          *                             the instance is configured for.
18424          * @static
18425          */
18426         getLocation: function(oDD) {
18427             if (! this.isTypeOfDD(oDD)) {
18428                 return null;
18429             }
18430
18431             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18432
18433             try {
18434                 pos= Roo.lib.Dom.getXY(el);
18435             } catch (e) { }
18436
18437             if (!pos) {
18438                 return null;
18439             }
18440
18441             x1 = pos[0];
18442             x2 = x1 + el.offsetWidth;
18443             y1 = pos[1];
18444             y2 = y1 + el.offsetHeight;
18445
18446             t = y1 - oDD.padding[0];
18447             r = x2 + oDD.padding[1];
18448             b = y2 + oDD.padding[2];
18449             l = x1 - oDD.padding[3];
18450
18451             return new Roo.lib.Region( t, r, b, l );
18452         },
18453
18454         /**
18455          * Checks the cursor location to see if it over the target
18456          * @method isOverTarget
18457          * @param {Roo.lib.Point} pt The point to evaluate
18458          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18459          * @return {boolean} true if the mouse is over the target
18460          * @private
18461          * @static
18462          */
18463         isOverTarget: function(pt, oTarget, intersect) {
18464             // use cache if available
18465             var loc = this.locationCache[oTarget.id];
18466             if (!loc || !this.useCache) {
18467                 loc = this.getLocation(oTarget);
18468                 this.locationCache[oTarget.id] = loc;
18469
18470             }
18471
18472             if (!loc) {
18473                 return false;
18474             }
18475
18476             oTarget.cursorIsOver = loc.contains( pt );
18477
18478             // DragDrop is using this as a sanity check for the initial mousedown
18479             // in this case we are done.  In POINT mode, if the drag obj has no
18480             // contraints, we are also done. Otherwise we need to evaluate the
18481             // location of the target as related to the actual location of the
18482             // dragged element.
18483             var dc = this.dragCurrent;
18484             if (!dc || !dc.getTargetCoord ||
18485                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18486                 return oTarget.cursorIsOver;
18487             }
18488
18489             oTarget.overlap = null;
18490
18491             // Get the current location of the drag element, this is the
18492             // location of the mouse event less the delta that represents
18493             // where the original mousedown happened on the element.  We
18494             // need to consider constraints and ticks as well.
18495             var pos = dc.getTargetCoord(pt.x, pt.y);
18496
18497             var el = dc.getDragEl();
18498             var curRegion = new Roo.lib.Region( pos.y,
18499                                                    pos.x + el.offsetWidth,
18500                                                    pos.y + el.offsetHeight,
18501                                                    pos.x );
18502
18503             var overlap = curRegion.intersect(loc);
18504
18505             if (overlap) {
18506                 oTarget.overlap = overlap;
18507                 return (intersect) ? true : oTarget.cursorIsOver;
18508             } else {
18509                 return false;
18510             }
18511         },
18512
18513         /**
18514          * unload event handler
18515          * @method _onUnload
18516          * @private
18517          * @static
18518          */
18519         _onUnload: function(e, me) {
18520             Roo.dd.DragDropMgr.unregAll();
18521         },
18522
18523         /**
18524          * Cleans up the drag and drop events and objects.
18525          * @method unregAll
18526          * @private
18527          * @static
18528          */
18529         unregAll: function() {
18530
18531             if (this.dragCurrent) {
18532                 this.stopDrag();
18533                 this.dragCurrent = null;
18534             }
18535
18536             this._execOnAll("unreg", []);
18537
18538             for (i in this.elementCache) {
18539                 delete this.elementCache[i];
18540             }
18541
18542             this.elementCache = {};
18543             this.ids = {};
18544         },
18545
18546         /**
18547          * A cache of DOM elements
18548          * @property elementCache
18549          * @private
18550          * @static
18551          */
18552         elementCache: {},
18553
18554         /**
18555          * Get the wrapper for the DOM element specified
18556          * @method getElWrapper
18557          * @param {String} id the id of the element to get
18558          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18559          * @private
18560          * @deprecated This wrapper isn't that useful
18561          * @static
18562          */
18563         getElWrapper: function(id) {
18564             var oWrapper = this.elementCache[id];
18565             if (!oWrapper || !oWrapper.el) {
18566                 oWrapper = this.elementCache[id] =
18567                     new this.ElementWrapper(Roo.getDom(id));
18568             }
18569             return oWrapper;
18570         },
18571
18572         /**
18573          * Returns the actual DOM element
18574          * @method getElement
18575          * @param {String} id the id of the elment to get
18576          * @return {Object} The element
18577          * @deprecated use Roo.getDom instead
18578          * @static
18579          */
18580         getElement: function(id) {
18581             return Roo.getDom(id);
18582         },
18583
18584         /**
18585          * Returns the style property for the DOM element (i.e.,
18586          * document.getElById(id).style)
18587          * @method getCss
18588          * @param {String} id the id of the elment to get
18589          * @return {Object} The style property of the element
18590          * @deprecated use Roo.getDom instead
18591          * @static
18592          */
18593         getCss: function(id) {
18594             var el = Roo.getDom(id);
18595             return (el) ? el.style : null;
18596         },
18597
18598         /**
18599          * Inner class for cached elements
18600          * @class DragDropMgr.ElementWrapper
18601          * @for DragDropMgr
18602          * @private
18603          * @deprecated
18604          */
18605         ElementWrapper: function(el) {
18606                 /**
18607                  * The element
18608                  * @property el
18609                  */
18610                 this.el = el || null;
18611                 /**
18612                  * The element id
18613                  * @property id
18614                  */
18615                 this.id = this.el && el.id;
18616                 /**
18617                  * A reference to the style property
18618                  * @property css
18619                  */
18620                 this.css = this.el && el.style;
18621             },
18622
18623         /**
18624          * Returns the X position of an html element
18625          * @method getPosX
18626          * @param el the element for which to get the position
18627          * @return {int} the X coordinate
18628          * @for DragDropMgr
18629          * @deprecated use Roo.lib.Dom.getX instead
18630          * @static
18631          */
18632         getPosX: function(el) {
18633             return Roo.lib.Dom.getX(el);
18634         },
18635
18636         /**
18637          * Returns the Y position of an html element
18638          * @method getPosY
18639          * @param el the element for which to get the position
18640          * @return {int} the Y coordinate
18641          * @deprecated use Roo.lib.Dom.getY instead
18642          * @static
18643          */
18644         getPosY: function(el) {
18645             return Roo.lib.Dom.getY(el);
18646         },
18647
18648         /**
18649          * Swap two nodes.  In IE, we use the native method, for others we
18650          * emulate the IE behavior
18651          * @method swapNode
18652          * @param n1 the first node to swap
18653          * @param n2 the other node to swap
18654          * @static
18655          */
18656         swapNode: function(n1, n2) {
18657             if (n1.swapNode) {
18658                 n1.swapNode(n2);
18659             } else {
18660                 var p = n2.parentNode;
18661                 var s = n2.nextSibling;
18662
18663                 if (s == n1) {
18664                     p.insertBefore(n1, n2);
18665                 } else if (n2 == n1.nextSibling) {
18666                     p.insertBefore(n2, n1);
18667                 } else {
18668                     n1.parentNode.replaceChild(n2, n1);
18669                     p.insertBefore(n1, s);
18670                 }
18671             }
18672         },
18673
18674         /**
18675          * Returns the current scroll position
18676          * @method getScroll
18677          * @private
18678          * @static
18679          */
18680         getScroll: function () {
18681             var t, l, dde=document.documentElement, db=document.body;
18682             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18683                 t = dde.scrollTop;
18684                 l = dde.scrollLeft;
18685             } else if (db) {
18686                 t = db.scrollTop;
18687                 l = db.scrollLeft;
18688             } else {
18689
18690             }
18691             return { top: t, left: l };
18692         },
18693
18694         /**
18695          * Returns the specified element style property
18696          * @method getStyle
18697          * @param {HTMLElement} el          the element
18698          * @param {string}      styleProp   the style property
18699          * @return {string} The value of the style property
18700          * @deprecated use Roo.lib.Dom.getStyle
18701          * @static
18702          */
18703         getStyle: function(el, styleProp) {
18704             return Roo.fly(el).getStyle(styleProp);
18705         },
18706
18707         /**
18708          * Gets the scrollTop
18709          * @method getScrollTop
18710          * @return {int} the document's scrollTop
18711          * @static
18712          */
18713         getScrollTop: function () { return this.getScroll().top; },
18714
18715         /**
18716          * Gets the scrollLeft
18717          * @method getScrollLeft
18718          * @return {int} the document's scrollTop
18719          * @static
18720          */
18721         getScrollLeft: function () { return this.getScroll().left; },
18722
18723         /**
18724          * Sets the x/y position of an element to the location of the
18725          * target element.
18726          * @method moveToEl
18727          * @param {HTMLElement} moveEl      The element to move
18728          * @param {HTMLElement} targetEl    The position reference element
18729          * @static
18730          */
18731         moveToEl: function (moveEl, targetEl) {
18732             var aCoord = Roo.lib.Dom.getXY(targetEl);
18733             Roo.lib.Dom.setXY(moveEl, aCoord);
18734         },
18735
18736         /**
18737          * Numeric array sort function
18738          * @method numericSort
18739          * @static
18740          */
18741         numericSort: function(a, b) { return (a - b); },
18742
18743         /**
18744          * Internal counter
18745          * @property _timeoutCount
18746          * @private
18747          * @static
18748          */
18749         _timeoutCount: 0,
18750
18751         /**
18752          * Trying to make the load order less important.  Without this we get
18753          * an error if this file is loaded before the Event Utility.
18754          * @method _addListeners
18755          * @private
18756          * @static
18757          */
18758         _addListeners: function() {
18759             var DDM = Roo.dd.DDM;
18760             if ( Roo.lib.Event && document ) {
18761                 DDM._onLoad();
18762             } else {
18763                 if (DDM._timeoutCount > 2000) {
18764                 } else {
18765                     setTimeout(DDM._addListeners, 10);
18766                     if (document && document.body) {
18767                         DDM._timeoutCount += 1;
18768                     }
18769                 }
18770             }
18771         },
18772
18773         /**
18774          * Recursively searches the immediate parent and all child nodes for
18775          * the handle element in order to determine wheter or not it was
18776          * clicked.
18777          * @method handleWasClicked
18778          * @param node the html element to inspect
18779          * @static
18780          */
18781         handleWasClicked: function(node, id) {
18782             if (this.isHandle(id, node.id)) {
18783                 return true;
18784             } else {
18785                 // check to see if this is a text node child of the one we want
18786                 var p = node.parentNode;
18787
18788                 while (p) {
18789                     if (this.isHandle(id, p.id)) {
18790                         return true;
18791                     } else {
18792                         p = p.parentNode;
18793                     }
18794                 }
18795             }
18796
18797             return false;
18798         }
18799
18800     };
18801
18802 }();
18803
18804 // shorter alias, save a few bytes
18805 Roo.dd.DDM = Roo.dd.DragDropMgr;
18806 Roo.dd.DDM._addListeners();
18807
18808 }/*
18809  * Based on:
18810  * Ext JS Library 1.1.1
18811  * Copyright(c) 2006-2007, Ext JS, LLC.
18812  *
18813  * Originally Released Under LGPL - original licence link has changed is not relivant.
18814  *
18815  * Fork - LGPL
18816  * <script type="text/javascript">
18817  */
18818
18819 /**
18820  * @class Roo.dd.DD
18821  * A DragDrop implementation where the linked element follows the
18822  * mouse cursor during a drag.
18823  * @extends Roo.dd.DragDrop
18824  * @constructor
18825  * @param {String} id the id of the linked element
18826  * @param {String} sGroup the group of related DragDrop items
18827  * @param {object} config an object containing configurable attributes
18828  *                Valid properties for DD:
18829  *                    scroll
18830  */
18831 Roo.dd.DD = function(id, sGroup, config) {
18832     if (id) {
18833         this.init(id, sGroup, config);
18834     }
18835 };
18836
18837 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18838
18839     /**
18840      * When set to true, the utility automatically tries to scroll the browser
18841      * window wehn a drag and drop element is dragged near the viewport boundary.
18842      * Defaults to true.
18843      * @property scroll
18844      * @type boolean
18845      */
18846     scroll: true,
18847
18848     /**
18849      * Sets the pointer offset to the distance between the linked element's top
18850      * left corner and the location the element was clicked
18851      * @method autoOffset
18852      * @param {int} iPageX the X coordinate of the click
18853      * @param {int} iPageY the Y coordinate of the click
18854      */
18855     autoOffset: function(iPageX, iPageY) {
18856         var x = iPageX - this.startPageX;
18857         var y = iPageY - this.startPageY;
18858         this.setDelta(x, y);
18859     },
18860
18861     /**
18862      * Sets the pointer offset.  You can call this directly to force the
18863      * offset to be in a particular location (e.g., pass in 0,0 to set it
18864      * to the center of the object)
18865      * @method setDelta
18866      * @param {int} iDeltaX the distance from the left
18867      * @param {int} iDeltaY the distance from the top
18868      */
18869     setDelta: function(iDeltaX, iDeltaY) {
18870         this.deltaX = iDeltaX;
18871         this.deltaY = iDeltaY;
18872     },
18873
18874     /**
18875      * Sets the drag element to the location of the mousedown or click event,
18876      * maintaining the cursor location relative to the location on the element
18877      * that was clicked.  Override this if you want to place the element in a
18878      * location other than where the cursor is.
18879      * @method setDragElPos
18880      * @param {int} iPageX the X coordinate of the mousedown or drag event
18881      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18882      */
18883     setDragElPos: function(iPageX, iPageY) {
18884         // the first time we do this, we are going to check to make sure
18885         // the element has css positioning
18886
18887         var el = this.getDragEl();
18888         this.alignElWithMouse(el, iPageX, iPageY);
18889     },
18890
18891     /**
18892      * Sets the element to the location of the mousedown or click event,
18893      * maintaining the cursor location relative to the location on the element
18894      * that was clicked.  Override this if you want to place the element in a
18895      * location other than where the cursor is.
18896      * @method alignElWithMouse
18897      * @param {HTMLElement} el the element to move
18898      * @param {int} iPageX the X coordinate of the mousedown or drag event
18899      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18900      */
18901     alignElWithMouse: function(el, iPageX, iPageY) {
18902         var oCoord = this.getTargetCoord(iPageX, iPageY);
18903         var fly = el.dom ? el : Roo.fly(el);
18904         if (!this.deltaSetXY) {
18905             var aCoord = [oCoord.x, oCoord.y];
18906             fly.setXY(aCoord);
18907             var newLeft = fly.getLeft(true);
18908             var newTop  = fly.getTop(true);
18909             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18910         } else {
18911             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18912         }
18913
18914         this.cachePosition(oCoord.x, oCoord.y);
18915         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18916         return oCoord;
18917     },
18918
18919     /**
18920      * Saves the most recent position so that we can reset the constraints and
18921      * tick marks on-demand.  We need to know this so that we can calculate the
18922      * number of pixels the element is offset from its original position.
18923      * @method cachePosition
18924      * @param iPageX the current x position (optional, this just makes it so we
18925      * don't have to look it up again)
18926      * @param iPageY the current y position (optional, this just makes it so we
18927      * don't have to look it up again)
18928      */
18929     cachePosition: function(iPageX, iPageY) {
18930         if (iPageX) {
18931             this.lastPageX = iPageX;
18932             this.lastPageY = iPageY;
18933         } else {
18934             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18935             this.lastPageX = aCoord[0];
18936             this.lastPageY = aCoord[1];
18937         }
18938     },
18939
18940     /**
18941      * Auto-scroll the window if the dragged object has been moved beyond the
18942      * visible window boundary.
18943      * @method autoScroll
18944      * @param {int} x the drag element's x position
18945      * @param {int} y the drag element's y position
18946      * @param {int} h the height of the drag element
18947      * @param {int} w the width of the drag element
18948      * @private
18949      */
18950     autoScroll: function(x, y, h, w) {
18951
18952         if (this.scroll) {
18953             // The client height
18954             var clientH = Roo.lib.Dom.getViewWidth();
18955
18956             // The client width
18957             var clientW = Roo.lib.Dom.getViewHeight();
18958
18959             // The amt scrolled down
18960             var st = this.DDM.getScrollTop();
18961
18962             // The amt scrolled right
18963             var sl = this.DDM.getScrollLeft();
18964
18965             // Location of the bottom of the element
18966             var bot = h + y;
18967
18968             // Location of the right of the element
18969             var right = w + x;
18970
18971             // The distance from the cursor to the bottom of the visible area,
18972             // adjusted so that we don't scroll if the cursor is beyond the
18973             // element drag constraints
18974             var toBot = (clientH + st - y - this.deltaY);
18975
18976             // The distance from the cursor to the right of the visible area
18977             var toRight = (clientW + sl - x - this.deltaX);
18978
18979
18980             // How close to the edge the cursor must be before we scroll
18981             // var thresh = (document.all) ? 100 : 40;
18982             var thresh = 40;
18983
18984             // How many pixels to scroll per autoscroll op.  This helps to reduce
18985             // clunky scrolling. IE is more sensitive about this ... it needs this
18986             // value to be higher.
18987             var scrAmt = (document.all) ? 80 : 30;
18988
18989             // Scroll down if we are near the bottom of the visible page and the
18990             // obj extends below the crease
18991             if ( bot > clientH && toBot < thresh ) {
18992                 window.scrollTo(sl, st + scrAmt);
18993             }
18994
18995             // Scroll up if the window is scrolled down and the top of the object
18996             // goes above the top border
18997             if ( y < st && st > 0 && y - st < thresh ) {
18998                 window.scrollTo(sl, st - scrAmt);
18999             }
19000
19001             // Scroll right if the obj is beyond the right border and the cursor is
19002             // near the border.
19003             if ( right > clientW && toRight < thresh ) {
19004                 window.scrollTo(sl + scrAmt, st);
19005             }
19006
19007             // Scroll left if the window has been scrolled to the right and the obj
19008             // extends past the left border
19009             if ( x < sl && sl > 0 && x - sl < thresh ) {
19010                 window.scrollTo(sl - scrAmt, st);
19011             }
19012         }
19013     },
19014
19015     /**
19016      * Finds the location the element should be placed if we want to move
19017      * it to where the mouse location less the click offset would place us.
19018      * @method getTargetCoord
19019      * @param {int} iPageX the X coordinate of the click
19020      * @param {int} iPageY the Y coordinate of the click
19021      * @return an object that contains the coordinates (Object.x and Object.y)
19022      * @private
19023      */
19024     getTargetCoord: function(iPageX, iPageY) {
19025
19026
19027         var x = iPageX - this.deltaX;
19028         var y = iPageY - this.deltaY;
19029
19030         if (this.constrainX) {
19031             if (x < this.minX) { x = this.minX; }
19032             if (x > this.maxX) { x = this.maxX; }
19033         }
19034
19035         if (this.constrainY) {
19036             if (y < this.minY) { y = this.minY; }
19037             if (y > this.maxY) { y = this.maxY; }
19038         }
19039
19040         x = this.getTick(x, this.xTicks);
19041         y = this.getTick(y, this.yTicks);
19042
19043
19044         return {x:x, y:y};
19045     },
19046
19047     /*
19048      * Sets up config options specific to this class. Overrides
19049      * Roo.dd.DragDrop, but all versions of this method through the
19050      * inheritance chain are called
19051      */
19052     applyConfig: function() {
19053         Roo.dd.DD.superclass.applyConfig.call(this);
19054         this.scroll = (this.config.scroll !== false);
19055     },
19056
19057     /*
19058      * Event that fires prior to the onMouseDown event.  Overrides
19059      * Roo.dd.DragDrop.
19060      */
19061     b4MouseDown: function(e) {
19062         // this.resetConstraints();
19063         this.autoOffset(e.getPageX(),
19064                             e.getPageY());
19065     },
19066
19067     /*
19068      * Event that fires prior to the onDrag event.  Overrides
19069      * Roo.dd.DragDrop.
19070      */
19071     b4Drag: function(e) {
19072         this.setDragElPos(e.getPageX(),
19073                             e.getPageY());
19074     },
19075
19076     toString: function() {
19077         return ("DD " + this.id);
19078     }
19079
19080     //////////////////////////////////////////////////////////////////////////
19081     // Debugging ygDragDrop events that can be overridden
19082     //////////////////////////////////////////////////////////////////////////
19083     /*
19084     startDrag: function(x, y) {
19085     },
19086
19087     onDrag: function(e) {
19088     },
19089
19090     onDragEnter: function(e, id) {
19091     },
19092
19093     onDragOver: function(e, id) {
19094     },
19095
19096     onDragOut: function(e, id) {
19097     },
19098
19099     onDragDrop: function(e, id) {
19100     },
19101
19102     endDrag: function(e) {
19103     }
19104
19105     */
19106
19107 });/*
19108  * Based on:
19109  * Ext JS Library 1.1.1
19110  * Copyright(c) 2006-2007, Ext JS, LLC.
19111  *
19112  * Originally Released Under LGPL - original licence link has changed is not relivant.
19113  *
19114  * Fork - LGPL
19115  * <script type="text/javascript">
19116  */
19117
19118 /**
19119  * @class Roo.dd.DDProxy
19120  * A DragDrop implementation that inserts an empty, bordered div into
19121  * the document that follows the cursor during drag operations.  At the time of
19122  * the click, the frame div is resized to the dimensions of the linked html
19123  * element, and moved to the exact location of the linked element.
19124  *
19125  * References to the "frame" element refer to the single proxy element that
19126  * was created to be dragged in place of all DDProxy elements on the
19127  * page.
19128  *
19129  * @extends Roo.dd.DD
19130  * @constructor
19131  * @param {String} id the id of the linked html element
19132  * @param {String} sGroup the group of related DragDrop objects
19133  * @param {object} config an object containing configurable attributes
19134  *                Valid properties for DDProxy in addition to those in DragDrop:
19135  *                   resizeFrame, centerFrame, dragElId
19136  */
19137 Roo.dd.DDProxy = function(id, sGroup, config) {
19138     if (id) {
19139         this.init(id, sGroup, config);
19140         this.initFrame();
19141     }
19142 };
19143
19144 /**
19145  * The default drag frame div id
19146  * @property Roo.dd.DDProxy.dragElId
19147  * @type String
19148  * @static
19149  */
19150 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19151
19152 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19153
19154     /**
19155      * By default we resize the drag frame to be the same size as the element
19156      * we want to drag (this is to get the frame effect).  We can turn it off
19157      * if we want a different behavior.
19158      * @property resizeFrame
19159      * @type boolean
19160      */
19161     resizeFrame: true,
19162
19163     /**
19164      * By default the frame is positioned exactly where the drag element is, so
19165      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19166      * you do not have constraints on the obj is to have the drag frame centered
19167      * around the cursor.  Set centerFrame to true for this effect.
19168      * @property centerFrame
19169      * @type boolean
19170      */
19171     centerFrame: false,
19172
19173     /**
19174      * Creates the proxy element if it does not yet exist
19175      * @method createFrame
19176      */
19177     createFrame: function() {
19178         var self = this;
19179         var body = document.body;
19180
19181         if (!body || !body.firstChild) {
19182             setTimeout( function() { self.createFrame(); }, 50 );
19183             return;
19184         }
19185
19186         var div = this.getDragEl();
19187
19188         if (!div) {
19189             div    = document.createElement("div");
19190             div.id = this.dragElId;
19191             var s  = div.style;
19192
19193             s.position   = "absolute";
19194             s.visibility = "hidden";
19195             s.cursor     = "move";
19196             s.border     = "2px solid #aaa";
19197             s.zIndex     = 999;
19198
19199             // appendChild can blow up IE if invoked prior to the window load event
19200             // while rendering a table.  It is possible there are other scenarios
19201             // that would cause this to happen as well.
19202             body.insertBefore(div, body.firstChild);
19203         }
19204     },
19205
19206     /**
19207      * Initialization for the drag frame element.  Must be called in the
19208      * constructor of all subclasses
19209      * @method initFrame
19210      */
19211     initFrame: function() {
19212         this.createFrame();
19213     },
19214
19215     applyConfig: function() {
19216         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19217
19218         this.resizeFrame = (this.config.resizeFrame !== false);
19219         this.centerFrame = (this.config.centerFrame);
19220         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19221     },
19222
19223     /**
19224      * Resizes the drag frame to the dimensions of the clicked object, positions
19225      * it over the object, and finally displays it
19226      * @method showFrame
19227      * @param {int} iPageX X click position
19228      * @param {int} iPageY Y click position
19229      * @private
19230      */
19231     showFrame: function(iPageX, iPageY) {
19232         var el = this.getEl();
19233         var dragEl = this.getDragEl();
19234         var s = dragEl.style;
19235
19236         this._resizeProxy();
19237
19238         if (this.centerFrame) {
19239             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19240                            Math.round(parseInt(s.height, 10)/2) );
19241         }
19242
19243         this.setDragElPos(iPageX, iPageY);
19244
19245         Roo.fly(dragEl).show();
19246     },
19247
19248     /**
19249      * The proxy is automatically resized to the dimensions of the linked
19250      * element when a drag is initiated, unless resizeFrame is set to false
19251      * @method _resizeProxy
19252      * @private
19253      */
19254     _resizeProxy: function() {
19255         if (this.resizeFrame) {
19256             var el = this.getEl();
19257             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19258         }
19259     },
19260
19261     // overrides Roo.dd.DragDrop
19262     b4MouseDown: function(e) {
19263         var x = e.getPageX();
19264         var y = e.getPageY();
19265         this.autoOffset(x, y);
19266         this.setDragElPos(x, y);
19267     },
19268
19269     // overrides Roo.dd.DragDrop
19270     b4StartDrag: function(x, y) {
19271         // show the drag frame
19272         this.showFrame(x, y);
19273     },
19274
19275     // overrides Roo.dd.DragDrop
19276     b4EndDrag: function(e) {
19277         Roo.fly(this.getDragEl()).hide();
19278     },
19279
19280     // overrides Roo.dd.DragDrop
19281     // By default we try to move the element to the last location of the frame.
19282     // This is so that the default behavior mirrors that of Roo.dd.DD.
19283     endDrag: function(e) {
19284
19285         var lel = this.getEl();
19286         var del = this.getDragEl();
19287
19288         // Show the drag frame briefly so we can get its position
19289         del.style.visibility = "";
19290
19291         this.beforeMove();
19292         // Hide the linked element before the move to get around a Safari
19293         // rendering bug.
19294         lel.style.visibility = "hidden";
19295         Roo.dd.DDM.moveToEl(lel, del);
19296         del.style.visibility = "hidden";
19297         lel.style.visibility = "";
19298
19299         this.afterDrag();
19300     },
19301
19302     beforeMove : function(){
19303
19304     },
19305
19306     afterDrag : function(){
19307
19308     },
19309
19310     toString: function() {
19311         return ("DDProxy " + this.id);
19312     }
19313
19314 });
19315 /*
19316  * Based on:
19317  * Ext JS Library 1.1.1
19318  * Copyright(c) 2006-2007, Ext JS, LLC.
19319  *
19320  * Originally Released Under LGPL - original licence link has changed is not relivant.
19321  *
19322  * Fork - LGPL
19323  * <script type="text/javascript">
19324  */
19325
19326  /**
19327  * @class Roo.dd.DDTarget
19328  * A DragDrop implementation that does not move, but can be a drop
19329  * target.  You would get the same result by simply omitting implementation
19330  * for the event callbacks, but this way we reduce the processing cost of the
19331  * event listener and the callbacks.
19332  * @extends Roo.dd.DragDrop
19333  * @constructor
19334  * @param {String} id the id of the element that is a drop target
19335  * @param {String} sGroup the group of related DragDrop objects
19336  * @param {object} config an object containing configurable attributes
19337  *                 Valid properties for DDTarget in addition to those in
19338  *                 DragDrop:
19339  *                    none
19340  */
19341 Roo.dd.DDTarget = function(id, sGroup, config) {
19342     if (id) {
19343         this.initTarget(id, sGroup, config);
19344     }
19345     if (config.listeners || config.events) { 
19346        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19347             listeners : config.listeners || {}, 
19348             events : config.events || {} 
19349         });    
19350     }
19351 };
19352
19353 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19354 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19355     toString: function() {
19356         return ("DDTarget " + this.id);
19357     }
19358 });
19359 /*
19360  * Based on:
19361  * Ext JS Library 1.1.1
19362  * Copyright(c) 2006-2007, Ext JS, LLC.
19363  *
19364  * Originally Released Under LGPL - original licence link has changed is not relivant.
19365  *
19366  * Fork - LGPL
19367  * <script type="text/javascript">
19368  */
19369  
19370
19371 /**
19372  * @class Roo.dd.ScrollManager
19373  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19374  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19375  * @singleton
19376  */
19377 Roo.dd.ScrollManager = function(){
19378     var ddm = Roo.dd.DragDropMgr;
19379     var els = {};
19380     var dragEl = null;
19381     var proc = {};
19382     
19383     
19384     
19385     var onStop = function(e){
19386         dragEl = null;
19387         clearProc();
19388     };
19389     
19390     var triggerRefresh = function(){
19391         if(ddm.dragCurrent){
19392              ddm.refreshCache(ddm.dragCurrent.groups);
19393         }
19394     };
19395     
19396     var doScroll = function(){
19397         if(ddm.dragCurrent){
19398             var dds = Roo.dd.ScrollManager;
19399             if(!dds.animate){
19400                 if(proc.el.scroll(proc.dir, dds.increment)){
19401                     triggerRefresh();
19402                 }
19403             }else{
19404                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19405             }
19406         }
19407     };
19408     
19409     var clearProc = function(){
19410         if(proc.id){
19411             clearInterval(proc.id);
19412         }
19413         proc.id = 0;
19414         proc.el = null;
19415         proc.dir = "";
19416     };
19417     
19418     var startProc = function(el, dir){
19419          Roo.log('scroll startproc');
19420         clearProc();
19421         proc.el = el;
19422         proc.dir = dir;
19423         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19424     };
19425     
19426     var onFire = function(e, isDrop){
19427        
19428         if(isDrop || !ddm.dragCurrent){ return; }
19429         var dds = Roo.dd.ScrollManager;
19430         if(!dragEl || dragEl != ddm.dragCurrent){
19431             dragEl = ddm.dragCurrent;
19432             // refresh regions on drag start
19433             dds.refreshCache();
19434         }
19435         
19436         var xy = Roo.lib.Event.getXY(e);
19437         var pt = new Roo.lib.Point(xy[0], xy[1]);
19438         for(var id in els){
19439             var el = els[id], r = el._region;
19440             if(r && r.contains(pt) && el.isScrollable()){
19441                 if(r.bottom - pt.y <= dds.thresh){
19442                     if(proc.el != el){
19443                         startProc(el, "down");
19444                     }
19445                     return;
19446                 }else if(r.right - pt.x <= dds.thresh){
19447                     if(proc.el != el){
19448                         startProc(el, "left");
19449                     }
19450                     return;
19451                 }else if(pt.y - r.top <= dds.thresh){
19452                     if(proc.el != el){
19453                         startProc(el, "up");
19454                     }
19455                     return;
19456                 }else if(pt.x - r.left <= dds.thresh){
19457                     if(proc.el != el){
19458                         startProc(el, "right");
19459                     }
19460                     return;
19461                 }
19462             }
19463         }
19464         clearProc();
19465     };
19466     
19467     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19468     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19469     
19470     return {
19471         /**
19472          * Registers new overflow element(s) to auto scroll
19473          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19474          */
19475         register : function(el){
19476             if(el instanceof Array){
19477                 for(var i = 0, len = el.length; i < len; i++) {
19478                         this.register(el[i]);
19479                 }
19480             }else{
19481                 el = Roo.get(el);
19482                 els[el.id] = el;
19483             }
19484             Roo.dd.ScrollManager.els = els;
19485         },
19486         
19487         /**
19488          * Unregisters overflow element(s) so they are no longer scrolled
19489          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19490          */
19491         unregister : function(el){
19492             if(el instanceof Array){
19493                 for(var i = 0, len = el.length; i < len; i++) {
19494                         this.unregister(el[i]);
19495                 }
19496             }else{
19497                 el = Roo.get(el);
19498                 delete els[el.id];
19499             }
19500         },
19501         
19502         /**
19503          * The number of pixels from the edge of a container the pointer needs to be to 
19504          * trigger scrolling (defaults to 25)
19505          * @type Number
19506          */
19507         thresh : 25,
19508         
19509         /**
19510          * The number of pixels to scroll in each scroll increment (defaults to 50)
19511          * @type Number
19512          */
19513         increment : 100,
19514         
19515         /**
19516          * The frequency of scrolls in milliseconds (defaults to 500)
19517          * @type Number
19518          */
19519         frequency : 500,
19520         
19521         /**
19522          * True to animate the scroll (defaults to true)
19523          * @type Boolean
19524          */
19525         animate: true,
19526         
19527         /**
19528          * The animation duration in seconds - 
19529          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19530          * @type Number
19531          */
19532         animDuration: .4,
19533         
19534         /**
19535          * Manually trigger a cache refresh.
19536          */
19537         refreshCache : function(){
19538             for(var id in els){
19539                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19540                     els[id]._region = els[id].getRegion();
19541                 }
19542             }
19543         }
19544     };
19545 }();/*
19546  * Based on:
19547  * Ext JS Library 1.1.1
19548  * Copyright(c) 2006-2007, Ext JS, LLC.
19549  *
19550  * Originally Released Under LGPL - original licence link has changed is not relivant.
19551  *
19552  * Fork - LGPL
19553  * <script type="text/javascript">
19554  */
19555  
19556
19557 /**
19558  * @class Roo.dd.Registry
19559  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19560  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19561  * @singleton
19562  */
19563 Roo.dd.Registry = function(){
19564     var elements = {}; 
19565     var handles = {}; 
19566     var autoIdSeed = 0;
19567
19568     var getId = function(el, autogen){
19569         if(typeof el == "string"){
19570             return el;
19571         }
19572         var id = el.id;
19573         if(!id && autogen !== false){
19574             id = "roodd-" + (++autoIdSeed);
19575             el.id = id;
19576         }
19577         return id;
19578     };
19579     
19580     return {
19581     /**
19582      * Register a drag drop element
19583      * @param {String|HTMLElement} element The id or DOM node to register
19584      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19585      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19586      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19587      * populated in the data object (if applicable):
19588      * <pre>
19589 Value      Description<br />
19590 ---------  ------------------------------------------<br />
19591 handles    Array of DOM nodes that trigger dragging<br />
19592            for the element being registered<br />
19593 isHandle   True if the element passed in triggers<br />
19594            dragging itself, else false
19595 </pre>
19596      */
19597         register : function(el, data){
19598             data = data || {};
19599             if(typeof el == "string"){
19600                 el = document.getElementById(el);
19601             }
19602             data.ddel = el;
19603             elements[getId(el)] = data;
19604             if(data.isHandle !== false){
19605                 handles[data.ddel.id] = data;
19606             }
19607             if(data.handles){
19608                 var hs = data.handles;
19609                 for(var i = 0, len = hs.length; i < len; i++){
19610                         handles[getId(hs[i])] = data;
19611                 }
19612             }
19613         },
19614
19615     /**
19616      * Unregister a drag drop element
19617      * @param {String|HTMLElement}  element The id or DOM node to unregister
19618      */
19619         unregister : function(el){
19620             var id = getId(el, false);
19621             var data = elements[id];
19622             if(data){
19623                 delete elements[id];
19624                 if(data.handles){
19625                     var hs = data.handles;
19626                     for(var i = 0, len = hs.length; i < len; i++){
19627                         delete handles[getId(hs[i], false)];
19628                     }
19629                 }
19630             }
19631         },
19632
19633     /**
19634      * Returns the handle registered for a DOM Node by id
19635      * @param {String|HTMLElement} id The DOM node or id to look up
19636      * @return {Object} handle The custom handle data
19637      */
19638         getHandle : function(id){
19639             if(typeof id != "string"){ // must be element?
19640                 id = id.id;
19641             }
19642             return handles[id];
19643         },
19644
19645     /**
19646      * Returns the handle that is registered for the DOM node that is the target of the event
19647      * @param {Event} e The event
19648      * @return {Object} handle The custom handle data
19649      */
19650         getHandleFromEvent : function(e){
19651             var t = Roo.lib.Event.getTarget(e);
19652             return t ? handles[t.id] : null;
19653         },
19654
19655     /**
19656      * Returns a custom data object that is registered for a DOM node by id
19657      * @param {String|HTMLElement} id The DOM node or id to look up
19658      * @return {Object} data The custom data
19659      */
19660         getTarget : function(id){
19661             if(typeof id != "string"){ // must be element?
19662                 id = id.id;
19663             }
19664             return elements[id];
19665         },
19666
19667     /**
19668      * Returns a custom data object that is registered for the DOM node that is the target of the event
19669      * @param {Event} e The event
19670      * @return {Object} data The custom data
19671      */
19672         getTargetFromEvent : function(e){
19673             var t = Roo.lib.Event.getTarget(e);
19674             return t ? elements[t.id] || handles[t.id] : null;
19675         }
19676     };
19677 }();/*
19678  * Based on:
19679  * Ext JS Library 1.1.1
19680  * Copyright(c) 2006-2007, Ext JS, LLC.
19681  *
19682  * Originally Released Under LGPL - original licence link has changed is not relivant.
19683  *
19684  * Fork - LGPL
19685  * <script type="text/javascript">
19686  */
19687  
19688
19689 /**
19690  * @class Roo.dd.StatusProxy
19691  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19692  * default drag proxy used by all Roo.dd components.
19693  * @constructor
19694  * @param {Object} config
19695  */
19696 Roo.dd.StatusProxy = function(config){
19697     Roo.apply(this, config);
19698     this.id = this.id || Roo.id();
19699     this.el = new Roo.Layer({
19700         dh: {
19701             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19702                 {tag: "div", cls: "x-dd-drop-icon"},
19703                 {tag: "div", cls: "x-dd-drag-ghost"}
19704             ]
19705         }, 
19706         shadow: !config || config.shadow !== false
19707     });
19708     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19709     this.dropStatus = this.dropNotAllowed;
19710 };
19711
19712 Roo.dd.StatusProxy.prototype = {
19713     /**
19714      * @cfg {String} dropAllowed
19715      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19716      */
19717     dropAllowed : "x-dd-drop-ok",
19718     /**
19719      * @cfg {String} dropNotAllowed
19720      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19721      */
19722     dropNotAllowed : "x-dd-drop-nodrop",
19723
19724     /**
19725      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19726      * over the current target element.
19727      * @param {String} cssClass The css class for the new drop status indicator image
19728      */
19729     setStatus : function(cssClass){
19730         cssClass = cssClass || this.dropNotAllowed;
19731         if(this.dropStatus != cssClass){
19732             this.el.replaceClass(this.dropStatus, cssClass);
19733             this.dropStatus = cssClass;
19734         }
19735     },
19736
19737     /**
19738      * Resets the status indicator to the default dropNotAllowed value
19739      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19740      */
19741     reset : function(clearGhost){
19742         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19743         this.dropStatus = this.dropNotAllowed;
19744         if(clearGhost){
19745             this.ghost.update("");
19746         }
19747     },
19748
19749     /**
19750      * Updates the contents of the ghost element
19751      * @param {String} html The html that will replace the current innerHTML of the ghost element
19752      */
19753     update : function(html){
19754         if(typeof html == "string"){
19755             this.ghost.update(html);
19756         }else{
19757             this.ghost.update("");
19758             html.style.margin = "0";
19759             this.ghost.dom.appendChild(html);
19760         }
19761         // ensure float = none set?? cant remember why though.
19762         var el = this.ghost.dom.firstChild;
19763                 if(el){
19764                         Roo.fly(el).setStyle('float', 'none');
19765                 }
19766     },
19767     
19768     /**
19769      * Returns the underlying proxy {@link Roo.Layer}
19770      * @return {Roo.Layer} el
19771     */
19772     getEl : function(){
19773         return this.el;
19774     },
19775
19776     /**
19777      * Returns the ghost element
19778      * @return {Roo.Element} el
19779      */
19780     getGhost : function(){
19781         return this.ghost;
19782     },
19783
19784     /**
19785      * Hides the proxy
19786      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19787      */
19788     hide : function(clear){
19789         this.el.hide();
19790         if(clear){
19791             this.reset(true);
19792         }
19793     },
19794
19795     /**
19796      * Stops the repair animation if it's currently running
19797      */
19798     stop : function(){
19799         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19800             this.anim.stop();
19801         }
19802     },
19803
19804     /**
19805      * Displays this proxy
19806      */
19807     show : function(){
19808         this.el.show();
19809     },
19810
19811     /**
19812      * Force the Layer to sync its shadow and shim positions to the element
19813      */
19814     sync : function(){
19815         this.el.sync();
19816     },
19817
19818     /**
19819      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19820      * invalid drop operation by the item being dragged.
19821      * @param {Array} xy The XY position of the element ([x, y])
19822      * @param {Function} callback The function to call after the repair is complete
19823      * @param {Object} scope The scope in which to execute the callback
19824      */
19825     repair : function(xy, callback, scope){
19826         this.callback = callback;
19827         this.scope = scope;
19828         if(xy && this.animRepair !== false){
19829             this.el.addClass("x-dd-drag-repair");
19830             this.el.hideUnders(true);
19831             this.anim = this.el.shift({
19832                 duration: this.repairDuration || .5,
19833                 easing: 'easeOut',
19834                 xy: xy,
19835                 stopFx: true,
19836                 callback: this.afterRepair,
19837                 scope: this
19838             });
19839         }else{
19840             this.afterRepair();
19841         }
19842     },
19843
19844     // private
19845     afterRepair : function(){
19846         this.hide(true);
19847         if(typeof this.callback == "function"){
19848             this.callback.call(this.scope || this);
19849         }
19850         this.callback = null;
19851         this.scope = null;
19852     }
19853 };/*
19854  * Based on:
19855  * Ext JS Library 1.1.1
19856  * Copyright(c) 2006-2007, Ext JS, LLC.
19857  *
19858  * Originally Released Under LGPL - original licence link has changed is not relivant.
19859  *
19860  * Fork - LGPL
19861  * <script type="text/javascript">
19862  */
19863
19864 /**
19865  * @class Roo.dd.DragSource
19866  * @extends Roo.dd.DDProxy
19867  * A simple class that provides the basic implementation needed to make any element draggable.
19868  * @constructor
19869  * @param {String/HTMLElement/Element} el The container element
19870  * @param {Object} config
19871  */
19872 Roo.dd.DragSource = function(el, config){
19873     this.el = Roo.get(el);
19874     this.dragData = {};
19875     
19876     Roo.apply(this, config);
19877     
19878     if(!this.proxy){
19879         this.proxy = new Roo.dd.StatusProxy();
19880     }
19881
19882     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19883           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19884     
19885     this.dragging = false;
19886 };
19887
19888 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19889     /**
19890      * @cfg {String} dropAllowed
19891      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19892      */
19893     dropAllowed : "x-dd-drop-ok",
19894     /**
19895      * @cfg {String} dropNotAllowed
19896      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19897      */
19898     dropNotAllowed : "x-dd-drop-nodrop",
19899
19900     /**
19901      * Returns the data object associated with this drag source
19902      * @return {Object} data An object containing arbitrary data
19903      */
19904     getDragData : function(e){
19905         return this.dragData;
19906     },
19907
19908     // private
19909     onDragEnter : function(e, id){
19910         var target = Roo.dd.DragDropMgr.getDDById(id);
19911         this.cachedTarget = target;
19912         if(this.beforeDragEnter(target, e, id) !== false){
19913             if(target.isNotifyTarget){
19914                 var status = target.notifyEnter(this, e, this.dragData);
19915                 this.proxy.setStatus(status);
19916             }else{
19917                 this.proxy.setStatus(this.dropAllowed);
19918             }
19919             
19920             if(this.afterDragEnter){
19921                 /**
19922                  * An empty function by default, but provided so that you can perform a custom action
19923                  * when the dragged item enters the drop target by providing an implementation.
19924                  * @param {Roo.dd.DragDrop} target The drop target
19925                  * @param {Event} e The event object
19926                  * @param {String} id The id of the dragged element
19927                  * @method afterDragEnter
19928                  */
19929                 this.afterDragEnter(target, e, id);
19930             }
19931         }
19932     },
19933
19934     /**
19935      * An empty function by default, but provided so that you can perform a custom action
19936      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19937      * @param {Roo.dd.DragDrop} target The drop target
19938      * @param {Event} e The event object
19939      * @param {String} id The id of the dragged element
19940      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19941      */
19942     beforeDragEnter : function(target, e, id){
19943         return true;
19944     },
19945
19946     // private
19947     alignElWithMouse: function() {
19948         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19949         this.proxy.sync();
19950     },
19951
19952     // private
19953     onDragOver : function(e, id){
19954         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19955         if(this.beforeDragOver(target, e, id) !== false){
19956             if(target.isNotifyTarget){
19957                 var status = target.notifyOver(this, e, this.dragData);
19958                 this.proxy.setStatus(status);
19959             }
19960
19961             if(this.afterDragOver){
19962                 /**
19963                  * An empty function by default, but provided so that you can perform a custom action
19964                  * while the dragged item is over the drop target by providing an implementation.
19965                  * @param {Roo.dd.DragDrop} target The drop target
19966                  * @param {Event} e The event object
19967                  * @param {String} id The id of the dragged element
19968                  * @method afterDragOver
19969                  */
19970                 this.afterDragOver(target, e, id);
19971             }
19972         }
19973     },
19974
19975     /**
19976      * An empty function by default, but provided so that you can perform a custom action
19977      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19978      * @param {Roo.dd.DragDrop} target The drop target
19979      * @param {Event} e The event object
19980      * @param {String} id The id of the dragged element
19981      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19982      */
19983     beforeDragOver : function(target, e, id){
19984         return true;
19985     },
19986
19987     // private
19988     onDragOut : function(e, id){
19989         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19990         if(this.beforeDragOut(target, e, id) !== false){
19991             if(target.isNotifyTarget){
19992                 target.notifyOut(this, e, this.dragData);
19993             }
19994             this.proxy.reset();
19995             if(this.afterDragOut){
19996                 /**
19997                  * An empty function by default, but provided so that you can perform a custom action
19998                  * after the dragged item is dragged out of the target without dropping.
19999                  * @param {Roo.dd.DragDrop} target The drop target
20000                  * @param {Event} e The event object
20001                  * @param {String} id The id of the dragged element
20002                  * @method afterDragOut
20003                  */
20004                 this.afterDragOut(target, e, id);
20005             }
20006         }
20007         this.cachedTarget = null;
20008     },
20009
20010     /**
20011      * An empty function by default, but provided so that you can perform a custom action before the dragged
20012      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20013      * @param {Roo.dd.DragDrop} target The drop target
20014      * @param {Event} e The event object
20015      * @param {String} id The id of the dragged element
20016      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20017      */
20018     beforeDragOut : function(target, e, id){
20019         return true;
20020     },
20021     
20022     // private
20023     onDragDrop : function(e, id){
20024         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20025         if(this.beforeDragDrop(target, e, id) !== false){
20026             if(target.isNotifyTarget){
20027                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20028                     this.onValidDrop(target, e, id);
20029                 }else{
20030                     this.onInvalidDrop(target, e, id);
20031                 }
20032             }else{
20033                 this.onValidDrop(target, e, id);
20034             }
20035             
20036             if(this.afterDragDrop){
20037                 /**
20038                  * An empty function by default, but provided so that you can perform a custom action
20039                  * after a valid drag drop has occurred by providing an implementation.
20040                  * @param {Roo.dd.DragDrop} target The drop target
20041                  * @param {Event} e The event object
20042                  * @param {String} id The id of the dropped element
20043                  * @method afterDragDrop
20044                  */
20045                 this.afterDragDrop(target, e, id);
20046             }
20047         }
20048         delete this.cachedTarget;
20049     },
20050
20051     /**
20052      * An empty function by default, but provided so that you can perform a custom action before the dragged
20053      * item is dropped onto the target and optionally cancel the onDragDrop.
20054      * @param {Roo.dd.DragDrop} target The drop target
20055      * @param {Event} e The event object
20056      * @param {String} id The id of the dragged element
20057      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20058      */
20059     beforeDragDrop : function(target, e, id){
20060         return true;
20061     },
20062
20063     // private
20064     onValidDrop : function(target, e, id){
20065         this.hideProxy();
20066         if(this.afterValidDrop){
20067             /**
20068              * An empty function by default, but provided so that you can perform a custom action
20069              * after a valid drop has occurred by providing an implementation.
20070              * @param {Object} target The target DD 
20071              * @param {Event} e The event object
20072              * @param {String} id The id of the dropped element
20073              * @method afterInvalidDrop
20074              */
20075             this.afterValidDrop(target, e, id);
20076         }
20077     },
20078
20079     // private
20080     getRepairXY : function(e, data){
20081         return this.el.getXY();  
20082     },
20083
20084     // private
20085     onInvalidDrop : function(target, e, id){
20086         this.beforeInvalidDrop(target, e, id);
20087         if(this.cachedTarget){
20088             if(this.cachedTarget.isNotifyTarget){
20089                 this.cachedTarget.notifyOut(this, e, this.dragData);
20090             }
20091             this.cacheTarget = null;
20092         }
20093         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20094
20095         if(this.afterInvalidDrop){
20096             /**
20097              * An empty function by default, but provided so that you can perform a custom action
20098              * after an invalid drop has occurred by providing an implementation.
20099              * @param {Event} e The event object
20100              * @param {String} id The id of the dropped element
20101              * @method afterInvalidDrop
20102              */
20103             this.afterInvalidDrop(e, id);
20104         }
20105     },
20106
20107     // private
20108     afterRepair : function(){
20109         if(Roo.enableFx){
20110             this.el.highlight(this.hlColor || "c3daf9");
20111         }
20112         this.dragging = false;
20113     },
20114
20115     /**
20116      * An empty function by default, but provided so that you can perform a custom action after an invalid
20117      * drop has occurred.
20118      * @param {Roo.dd.DragDrop} target The drop target
20119      * @param {Event} e The event object
20120      * @param {String} id The id of the dragged element
20121      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20122      */
20123     beforeInvalidDrop : function(target, e, id){
20124         return true;
20125     },
20126
20127     // private
20128     handleMouseDown : function(e){
20129         if(this.dragging) {
20130             return;
20131         }
20132         var data = this.getDragData(e);
20133         if(data && this.onBeforeDrag(data, e) !== false){
20134             this.dragData = data;
20135             this.proxy.stop();
20136             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20137         } 
20138     },
20139
20140     /**
20141      * An empty function by default, but provided so that you can perform a custom action before the initial
20142      * drag event begins and optionally cancel it.
20143      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20144      * @param {Event} e The event object
20145      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20146      */
20147     onBeforeDrag : function(data, e){
20148         return true;
20149     },
20150
20151     /**
20152      * An empty function by default, but provided so that you can perform a custom action once the initial
20153      * drag event has begun.  The drag cannot be canceled from this function.
20154      * @param {Number} x The x position of the click on the dragged object
20155      * @param {Number} y The y position of the click on the dragged object
20156      */
20157     onStartDrag : Roo.emptyFn,
20158
20159     // private - YUI override
20160     startDrag : function(x, y){
20161         this.proxy.reset();
20162         this.dragging = true;
20163         this.proxy.update("");
20164         this.onInitDrag(x, y);
20165         this.proxy.show();
20166     },
20167
20168     // private
20169     onInitDrag : function(x, y){
20170         var clone = this.el.dom.cloneNode(true);
20171         clone.id = Roo.id(); // prevent duplicate ids
20172         this.proxy.update(clone);
20173         this.onStartDrag(x, y);
20174         return true;
20175     },
20176
20177     /**
20178      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20179      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20180      */
20181     getProxy : function(){
20182         return this.proxy;  
20183     },
20184
20185     /**
20186      * Hides the drag source's {@link Roo.dd.StatusProxy}
20187      */
20188     hideProxy : function(){
20189         this.proxy.hide();  
20190         this.proxy.reset(true);
20191         this.dragging = false;
20192     },
20193
20194     // private
20195     triggerCacheRefresh : function(){
20196         Roo.dd.DDM.refreshCache(this.groups);
20197     },
20198
20199     // private - override to prevent hiding
20200     b4EndDrag: function(e) {
20201     },
20202
20203     // private - override to prevent moving
20204     endDrag : function(e){
20205         this.onEndDrag(this.dragData, e);
20206     },
20207
20208     // private
20209     onEndDrag : function(data, e){
20210     },
20211     
20212     // private - pin to cursor
20213     autoOffset : function(x, y) {
20214         this.setDelta(-12, -20);
20215     }    
20216 });/*
20217  * Based on:
20218  * Ext JS Library 1.1.1
20219  * Copyright(c) 2006-2007, Ext JS, LLC.
20220  *
20221  * Originally Released Under LGPL - original licence link has changed is not relivant.
20222  *
20223  * Fork - LGPL
20224  * <script type="text/javascript">
20225  */
20226
20227
20228 /**
20229  * @class Roo.dd.DropTarget
20230  * @extends Roo.dd.DDTarget
20231  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20232  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20233  * @constructor
20234  * @param {String/HTMLElement/Element} el The container element
20235  * @param {Object} config
20236  */
20237 Roo.dd.DropTarget = function(el, config){
20238     this.el = Roo.get(el);
20239     
20240     var listeners = false; ;
20241     if (config && config.listeners) {
20242         listeners= config.listeners;
20243         delete config.listeners;
20244     }
20245     Roo.apply(this, config);
20246     
20247     if(this.containerScroll){
20248         Roo.dd.ScrollManager.register(this.el);
20249     }
20250     this.addEvents( {
20251          /**
20252          * @scope Roo.dd.DropTarget
20253          */
20254          
20255          /**
20256          * @event enter
20257          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20258          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20259          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20260          * 
20261          * IMPORTANT : it should set this.overClass and this.dropAllowed
20262          * 
20263          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20264          * @param {Event} e The event
20265          * @param {Object} data An object containing arbitrary data supplied by the drag source
20266          */
20267         "enter" : true,
20268         
20269          /**
20270          * @event over
20271          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20272          * This method will be called on every mouse movement while the drag source is over the drop target.
20273          * This default implementation simply returns the dropAllowed config value.
20274          * 
20275          * IMPORTANT : it should set this.dropAllowed
20276          * 
20277          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20278          * @param {Event} e The event
20279          * @param {Object} data An object containing arbitrary data supplied by the drag source
20280          
20281          */
20282         "over" : true,
20283         /**
20284          * @event out
20285          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20286          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20287          * overClass (if any) from the drop element.
20288          * 
20289          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20290          * @param {Event} e The event
20291          * @param {Object} data An object containing arbitrary data supplied by the drag source
20292          */
20293          "out" : true,
20294          
20295         /**
20296          * @event drop
20297          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20298          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20299          * implementation that does something to process the drop event and returns true so that the drag source's
20300          * repair action does not run.
20301          * 
20302          * IMPORTANT : it should set this.success
20303          * 
20304          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20305          * @param {Event} e The event
20306          * @param {Object} data An object containing arbitrary data supplied by the drag source
20307         */
20308          "drop" : true
20309     });
20310             
20311      
20312     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20313         this.el.dom, 
20314         this.ddGroup || this.group,
20315         {
20316             isTarget: true,
20317             listeners : listeners || {} 
20318            
20319         
20320         }
20321     );
20322
20323 };
20324
20325 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20326     /**
20327      * @cfg {String} overClass
20328      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20329      */
20330      /**
20331      * @cfg {String} ddGroup
20332      * The drag drop group to handle drop events for
20333      */
20334      
20335     /**
20336      * @cfg {String} dropAllowed
20337      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20338      */
20339     dropAllowed : "x-dd-drop-ok",
20340     /**
20341      * @cfg {String} dropNotAllowed
20342      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20343      */
20344     dropNotAllowed : "x-dd-drop-nodrop",
20345     /**
20346      * @cfg {boolean} success
20347      * set this after drop listener.. 
20348      */
20349     success : false,
20350     /**
20351      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20352      * if the drop point is valid for over/enter..
20353      */
20354     valid : false,
20355     // private
20356     isTarget : true,
20357
20358     // private
20359     isNotifyTarget : true,
20360     
20361     /**
20362      * @hide
20363      */
20364     notifyEnter : function(dd, e, data)
20365     {
20366         this.valid = true;
20367         this.fireEvent('enter', dd, e, data);
20368         if(this.overClass){
20369             this.el.addClass(this.overClass);
20370         }
20371         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20372             this.valid ? this.dropAllowed : this.dropNotAllowed
20373         );
20374     },
20375
20376     /**
20377      * @hide
20378      */
20379     notifyOver : function(dd, e, data)
20380     {
20381         this.valid = true;
20382         this.fireEvent('over', dd, e, data);
20383         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20384             this.valid ? this.dropAllowed : this.dropNotAllowed
20385         );
20386     },
20387
20388     /**
20389      * @hide
20390      */
20391     notifyOut : function(dd, e, data)
20392     {
20393         this.fireEvent('out', dd, e, data);
20394         if(this.overClass){
20395             this.el.removeClass(this.overClass);
20396         }
20397     },
20398
20399     /**
20400      * @hide
20401      */
20402     notifyDrop : function(dd, e, data)
20403     {
20404         this.success = false;
20405         this.fireEvent('drop', dd, e, data);
20406         return this.success;
20407     }
20408 });/*
20409  * Based on:
20410  * Ext JS Library 1.1.1
20411  * Copyright(c) 2006-2007, Ext JS, LLC.
20412  *
20413  * Originally Released Under LGPL - original licence link has changed is not relivant.
20414  *
20415  * Fork - LGPL
20416  * <script type="text/javascript">
20417  */
20418
20419
20420 /**
20421  * @class Roo.dd.DragZone
20422  * @extends Roo.dd.DragSource
20423  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20424  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20425  * @constructor
20426  * @param {String/HTMLElement/Element} el The container element
20427  * @param {Object} config
20428  */
20429 Roo.dd.DragZone = function(el, config){
20430     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20431     if(this.containerScroll){
20432         Roo.dd.ScrollManager.register(this.el);
20433     }
20434 };
20435
20436 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20437     /**
20438      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20439      * for auto scrolling during drag operations.
20440      */
20441     /**
20442      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20443      * method after a failed drop (defaults to "c3daf9" - light blue)
20444      */
20445
20446     /**
20447      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20448      * for a valid target to drag based on the mouse down. Override this method
20449      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20450      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20451      * @param {EventObject} e The mouse down event
20452      * @return {Object} The dragData
20453      */
20454     getDragData : function(e){
20455         return Roo.dd.Registry.getHandleFromEvent(e);
20456     },
20457     
20458     /**
20459      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20460      * this.dragData.ddel
20461      * @param {Number} x The x position of the click on the dragged object
20462      * @param {Number} y The y position of the click on the dragged object
20463      * @return {Boolean} true to continue the drag, false to cancel
20464      */
20465     onInitDrag : function(x, y){
20466         this.proxy.update(this.dragData.ddel.cloneNode(true));
20467         this.onStartDrag(x, y);
20468         return true;
20469     },
20470     
20471     /**
20472      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20473      */
20474     afterRepair : function(){
20475         if(Roo.enableFx){
20476             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20477         }
20478         this.dragging = false;
20479     },
20480
20481     /**
20482      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20483      * the XY of this.dragData.ddel
20484      * @param {EventObject} e The mouse up event
20485      * @return {Array} The xy location (e.g. [100, 200])
20486      */
20487     getRepairXY : function(e){
20488         return Roo.Element.fly(this.dragData.ddel).getXY();  
20489     }
20490 });/*
20491  * Based on:
20492  * Ext JS Library 1.1.1
20493  * Copyright(c) 2006-2007, Ext JS, LLC.
20494  *
20495  * Originally Released Under LGPL - original licence link has changed is not relivant.
20496  *
20497  * Fork - LGPL
20498  * <script type="text/javascript">
20499  */
20500 /**
20501  * @class Roo.dd.DropZone
20502  * @extends Roo.dd.DropTarget
20503  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20504  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20505  * @constructor
20506  * @param {String/HTMLElement/Element} el The container element
20507  * @param {Object} config
20508  */
20509 Roo.dd.DropZone = function(el, config){
20510     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20511 };
20512
20513 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20514     /**
20515      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20516      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20517      * provide your own custom lookup.
20518      * @param {Event} e The event
20519      * @return {Object} data The custom data
20520      */
20521     getTargetFromEvent : function(e){
20522         return Roo.dd.Registry.getTargetFromEvent(e);
20523     },
20524
20525     /**
20526      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20527      * that it has registered.  This method has no default implementation and should be overridden to provide
20528      * node-specific processing if necessary.
20529      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20530      * {@link #getTargetFromEvent} for this node)
20531      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20532      * @param {Event} e The event
20533      * @param {Object} data An object containing arbitrary data supplied by the drag source
20534      */
20535     onNodeEnter : function(n, dd, e, data){
20536         
20537     },
20538
20539     /**
20540      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20541      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20542      * overridden to provide the proper feedback.
20543      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20544      * {@link #getTargetFromEvent} for this node)
20545      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20546      * @param {Event} e The event
20547      * @param {Object} data An object containing arbitrary data supplied by the drag source
20548      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20549      * underlying {@link Roo.dd.StatusProxy} can be updated
20550      */
20551     onNodeOver : function(n, dd, e, data){
20552         return this.dropAllowed;
20553     },
20554
20555     /**
20556      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20557      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20558      * node-specific processing if necessary.
20559      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20560      * {@link #getTargetFromEvent} for this node)
20561      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20562      * @param {Event} e The event
20563      * @param {Object} data An object containing arbitrary data supplied by the drag source
20564      */
20565     onNodeOut : function(n, dd, e, data){
20566         
20567     },
20568
20569     /**
20570      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20571      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20572      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20573      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20574      * {@link #getTargetFromEvent} for this node)
20575      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20576      * @param {Event} e The event
20577      * @param {Object} data An object containing arbitrary data supplied by the drag source
20578      * @return {Boolean} True if the drop was valid, else false
20579      */
20580     onNodeDrop : function(n, dd, e, data){
20581         return false;
20582     },
20583
20584     /**
20585      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20586      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20587      * it should be overridden to provide the proper feedback if necessary.
20588      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20589      * @param {Event} e The event
20590      * @param {Object} data An object containing arbitrary data supplied by the drag source
20591      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20592      * underlying {@link Roo.dd.StatusProxy} can be updated
20593      */
20594     onContainerOver : function(dd, e, data){
20595         return this.dropNotAllowed;
20596     },
20597
20598     /**
20599      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20600      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20601      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20602      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20603      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20604      * @param {Event} e The event
20605      * @param {Object} data An object containing arbitrary data supplied by the drag source
20606      * @return {Boolean} True if the drop was valid, else false
20607      */
20608     onContainerDrop : function(dd, e, data){
20609         return false;
20610     },
20611
20612     /**
20613      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20614      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20615      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20616      * you should override this method and provide a custom implementation.
20617      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20618      * @param {Event} e The event
20619      * @param {Object} data An object containing arbitrary data supplied by the drag source
20620      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20621      * underlying {@link Roo.dd.StatusProxy} can be updated
20622      */
20623     notifyEnter : function(dd, e, data){
20624         return this.dropNotAllowed;
20625     },
20626
20627     /**
20628      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20629      * This method will be called on every mouse movement while the drag source is over the drop zone.
20630      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20631      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20632      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20633      * registered node, it will call {@link #onContainerOver}.
20634      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20635      * @param {Event} e The event
20636      * @param {Object} data An object containing arbitrary data supplied by the drag source
20637      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20638      * underlying {@link Roo.dd.StatusProxy} can be updated
20639      */
20640     notifyOver : function(dd, e, data){
20641         var n = this.getTargetFromEvent(e);
20642         if(!n){ // not over valid drop target
20643             if(this.lastOverNode){
20644                 this.onNodeOut(this.lastOverNode, dd, e, data);
20645                 this.lastOverNode = null;
20646             }
20647             return this.onContainerOver(dd, e, data);
20648         }
20649         if(this.lastOverNode != n){
20650             if(this.lastOverNode){
20651                 this.onNodeOut(this.lastOverNode, dd, e, data);
20652             }
20653             this.onNodeEnter(n, dd, e, data);
20654             this.lastOverNode = n;
20655         }
20656         return this.onNodeOver(n, dd, e, data);
20657     },
20658
20659     /**
20660      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20661      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20662      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20663      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20664      * @param {Event} e The event
20665      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20666      */
20667     notifyOut : function(dd, e, data){
20668         if(this.lastOverNode){
20669             this.onNodeOut(this.lastOverNode, dd, e, data);
20670             this.lastOverNode = null;
20671         }
20672     },
20673
20674     /**
20675      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20676      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20677      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20678      * otherwise it will call {@link #onContainerDrop}.
20679      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20680      * @param {Event} e The event
20681      * @param {Object} data An object containing arbitrary data supplied by the drag source
20682      * @return {Boolean} True if the drop was valid, else false
20683      */
20684     notifyDrop : function(dd, e, data){
20685         if(this.lastOverNode){
20686             this.onNodeOut(this.lastOverNode, dd, e, data);
20687             this.lastOverNode = null;
20688         }
20689         var n = this.getTargetFromEvent(e);
20690         return n ?
20691             this.onNodeDrop(n, dd, e, data) :
20692             this.onContainerDrop(dd, e, data);
20693     },
20694
20695     // private
20696     triggerCacheRefresh : function(){
20697         Roo.dd.DDM.refreshCache(this.groups);
20698     }  
20699 });/*
20700  * Based on:
20701  * Ext JS Library 1.1.1
20702  * Copyright(c) 2006-2007, Ext JS, LLC.
20703  *
20704  * Originally Released Under LGPL - original licence link has changed is not relivant.
20705  *
20706  * Fork - LGPL
20707  * <script type="text/javascript">
20708  */
20709
20710
20711 /**
20712  * @class Roo.data.SortTypes
20713  * @singleton
20714  * Defines the default sorting (casting?) comparison functions used when sorting data.
20715  */
20716 Roo.data.SortTypes = {
20717     /**
20718      * Default sort that does nothing
20719      * @param {Mixed} s The value being converted
20720      * @return {Mixed} The comparison value
20721      */
20722     none : function(s){
20723         return s;
20724     },
20725     
20726     /**
20727      * The regular expression used to strip tags
20728      * @type {RegExp}
20729      * @property
20730      */
20731     stripTagsRE : /<\/?[^>]+>/gi,
20732     
20733     /**
20734      * Strips all HTML tags to sort on text only
20735      * @param {Mixed} s The value being converted
20736      * @return {String} The comparison value
20737      */
20738     asText : function(s){
20739         return String(s).replace(this.stripTagsRE, "");
20740     },
20741     
20742     /**
20743      * Strips all HTML tags to sort on text only - Case insensitive
20744      * @param {Mixed} s The value being converted
20745      * @return {String} The comparison value
20746      */
20747     asUCText : function(s){
20748         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20749     },
20750     
20751     /**
20752      * Case insensitive string
20753      * @param {Mixed} s The value being converted
20754      * @return {String} The comparison value
20755      */
20756     asUCString : function(s) {
20757         return String(s).toUpperCase();
20758     },
20759     
20760     /**
20761      * Date sorting
20762      * @param {Mixed} s The value being converted
20763      * @return {Number} The comparison value
20764      */
20765     asDate : function(s) {
20766         if(!s){
20767             return 0;
20768         }
20769         if(s instanceof Date){
20770             return s.getTime();
20771         }
20772         return Date.parse(String(s));
20773     },
20774     
20775     /**
20776      * Float sorting
20777      * @param {Mixed} s The value being converted
20778      * @return {Float} The comparison value
20779      */
20780     asFloat : function(s) {
20781         var val = parseFloat(String(s).replace(/,/g, ""));
20782         if(isNaN(val)) val = 0;
20783         return val;
20784     },
20785     
20786     /**
20787      * Integer sorting
20788      * @param {Mixed} s The value being converted
20789      * @return {Number} The comparison value
20790      */
20791     asInt : function(s) {
20792         var val = parseInt(String(s).replace(/,/g, ""));
20793         if(isNaN(val)) val = 0;
20794         return val;
20795     }
20796 };/*
20797  * Based on:
20798  * Ext JS Library 1.1.1
20799  * Copyright(c) 2006-2007, Ext JS, LLC.
20800  *
20801  * Originally Released Under LGPL - original licence link has changed is not relivant.
20802  *
20803  * Fork - LGPL
20804  * <script type="text/javascript">
20805  */
20806
20807 /**
20808 * @class Roo.data.Record
20809  * Instances of this class encapsulate both record <em>definition</em> information, and record
20810  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20811  * to access Records cached in an {@link Roo.data.Store} object.<br>
20812  * <p>
20813  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20814  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20815  * objects.<br>
20816  * <p>
20817  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20818  * @constructor
20819  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20820  * {@link #create}. The parameters are the same.
20821  * @param {Array} data An associative Array of data values keyed by the field name.
20822  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20823  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20824  * not specified an integer id is generated.
20825  */
20826 Roo.data.Record = function(data, id){
20827     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20828     this.data = data;
20829 };
20830
20831 /**
20832  * Generate a constructor for a specific record layout.
20833  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20834  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20835  * Each field definition object may contain the following properties: <ul>
20836  * <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,
20837  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20838  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20839  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20840  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20841  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20842  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20843  * this may be omitted.</p></li>
20844  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20845  * <ul><li>auto (Default, implies no conversion)</li>
20846  * <li>string</li>
20847  * <li>int</li>
20848  * <li>float</li>
20849  * <li>boolean</li>
20850  * <li>date</li></ul></p></li>
20851  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20852  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20853  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20854  * by the Reader into an object that will be stored in the Record. It is passed the
20855  * following parameters:<ul>
20856  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20857  * </ul></p></li>
20858  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20859  * </ul>
20860  * <br>usage:<br><pre><code>
20861 var TopicRecord = Roo.data.Record.create(
20862     {name: 'title', mapping: 'topic_title'},
20863     {name: 'author', mapping: 'username'},
20864     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20865     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20866     {name: 'lastPoster', mapping: 'user2'},
20867     {name: 'excerpt', mapping: 'post_text'}
20868 );
20869
20870 var myNewRecord = new TopicRecord({
20871     title: 'Do my job please',
20872     author: 'noobie',
20873     totalPosts: 1,
20874     lastPost: new Date(),
20875     lastPoster: 'Animal',
20876     excerpt: 'No way dude!'
20877 });
20878 myStore.add(myNewRecord);
20879 </code></pre>
20880  * @method create
20881  * @static
20882  */
20883 Roo.data.Record.create = function(o){
20884     var f = function(){
20885         f.superclass.constructor.apply(this, arguments);
20886     };
20887     Roo.extend(f, Roo.data.Record);
20888     var p = f.prototype;
20889     p.fields = new Roo.util.MixedCollection(false, function(field){
20890         return field.name;
20891     });
20892     for(var i = 0, len = o.length; i < len; i++){
20893         p.fields.add(new Roo.data.Field(o[i]));
20894     }
20895     f.getField = function(name){
20896         return p.fields.get(name);  
20897     };
20898     return f;
20899 };
20900
20901 Roo.data.Record.AUTO_ID = 1000;
20902 Roo.data.Record.EDIT = 'edit';
20903 Roo.data.Record.REJECT = 'reject';
20904 Roo.data.Record.COMMIT = 'commit';
20905
20906 Roo.data.Record.prototype = {
20907     /**
20908      * Readonly flag - true if this record has been modified.
20909      * @type Boolean
20910      */
20911     dirty : false,
20912     editing : false,
20913     error: null,
20914     modified: null,
20915
20916     // private
20917     join : function(store){
20918         this.store = store;
20919     },
20920
20921     /**
20922      * Set the named field to the specified value.
20923      * @param {String} name The name of the field to set.
20924      * @param {Object} value The value to set the field to.
20925      */
20926     set : function(name, value){
20927         if(this.data[name] == value){
20928             return;
20929         }
20930         this.dirty = true;
20931         if(!this.modified){
20932             this.modified = {};
20933         }
20934         if(typeof this.modified[name] == 'undefined'){
20935             this.modified[name] = this.data[name];
20936         }
20937         this.data[name] = value;
20938         if(!this.editing && this.store){
20939             this.store.afterEdit(this);
20940         }       
20941     },
20942
20943     /**
20944      * Get the value of the named field.
20945      * @param {String} name The name of the field to get the value of.
20946      * @return {Object} The value of the field.
20947      */
20948     get : function(name){
20949         return this.data[name]; 
20950     },
20951
20952     // private
20953     beginEdit : function(){
20954         this.editing = true;
20955         this.modified = {}; 
20956     },
20957
20958     // private
20959     cancelEdit : function(){
20960         this.editing = false;
20961         delete this.modified;
20962     },
20963
20964     // private
20965     endEdit : function(){
20966         this.editing = false;
20967         if(this.dirty && this.store){
20968             this.store.afterEdit(this);
20969         }
20970     },
20971
20972     /**
20973      * Usually called by the {@link Roo.data.Store} which owns the Record.
20974      * Rejects all changes made to the Record since either creation, or the last commit operation.
20975      * Modified fields are reverted to their original values.
20976      * <p>
20977      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20978      * of reject operations.
20979      */
20980     reject : function(){
20981         var m = this.modified;
20982         for(var n in m){
20983             if(typeof m[n] != "function"){
20984                 this.data[n] = m[n];
20985             }
20986         }
20987         this.dirty = false;
20988         delete this.modified;
20989         this.editing = false;
20990         if(this.store){
20991             this.store.afterReject(this);
20992         }
20993     },
20994
20995     /**
20996      * Usually called by the {@link Roo.data.Store} which owns the Record.
20997      * Commits all changes made to the Record since either creation, or the last commit operation.
20998      * <p>
20999      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21000      * of commit operations.
21001      */
21002     commit : function(){
21003         this.dirty = false;
21004         delete this.modified;
21005         this.editing = false;
21006         if(this.store){
21007             this.store.afterCommit(this);
21008         }
21009     },
21010
21011     // private
21012     hasError : function(){
21013         return this.error != null;
21014     },
21015
21016     // private
21017     clearError : function(){
21018         this.error = null;
21019     },
21020
21021     /**
21022      * Creates a copy of this record.
21023      * @param {String} id (optional) A new record id if you don't want to use this record's id
21024      * @return {Record}
21025      */
21026     copy : function(newId) {
21027         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21028     }
21029 };/*
21030  * Based on:
21031  * Ext JS Library 1.1.1
21032  * Copyright(c) 2006-2007, Ext JS, LLC.
21033  *
21034  * Originally Released Under LGPL - original licence link has changed is not relivant.
21035  *
21036  * Fork - LGPL
21037  * <script type="text/javascript">
21038  */
21039
21040
21041
21042 /**
21043  * @class Roo.data.Store
21044  * @extends Roo.util.Observable
21045  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21046  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21047  * <p>
21048  * 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
21049  * has no knowledge of the format of the data returned by the Proxy.<br>
21050  * <p>
21051  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21052  * instances from the data object. These records are cached and made available through accessor functions.
21053  * @constructor
21054  * Creates a new Store.
21055  * @param {Object} config A config object containing the objects needed for the Store to access data,
21056  * and read the data into Records.
21057  */
21058 Roo.data.Store = function(config){
21059     this.data = new Roo.util.MixedCollection(false);
21060     this.data.getKey = function(o){
21061         return o.id;
21062     };
21063     this.baseParams = {};
21064     // private
21065     this.paramNames = {
21066         "start" : "start",
21067         "limit" : "limit",
21068         "sort" : "sort",
21069         "dir" : "dir",
21070         "multisort" : "_multisort"
21071     };
21072
21073     if(config && config.data){
21074         this.inlineData = config.data;
21075         delete config.data;
21076     }
21077
21078     Roo.apply(this, config);
21079     
21080     if(this.reader){ // reader passed
21081         this.reader = Roo.factory(this.reader, Roo.data);
21082         this.reader.xmodule = this.xmodule || false;
21083         if(!this.recordType){
21084             this.recordType = this.reader.recordType;
21085         }
21086         if(this.reader.onMetaChange){
21087             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21088         }
21089     }
21090
21091     if(this.recordType){
21092         this.fields = this.recordType.prototype.fields;
21093     }
21094     this.modified = [];
21095
21096     this.addEvents({
21097         /**
21098          * @event datachanged
21099          * Fires when the data cache has changed, and a widget which is using this Store
21100          * as a Record cache should refresh its view.
21101          * @param {Store} this
21102          */
21103         datachanged : true,
21104         /**
21105          * @event metachange
21106          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21107          * @param {Store} this
21108          * @param {Object} meta The JSON metadata
21109          */
21110         metachange : true,
21111         /**
21112          * @event add
21113          * Fires when Records have been added to the Store
21114          * @param {Store} this
21115          * @param {Roo.data.Record[]} records The array of Records added
21116          * @param {Number} index The index at which the record(s) were added
21117          */
21118         add : true,
21119         /**
21120          * @event remove
21121          * Fires when a Record has been removed from the Store
21122          * @param {Store} this
21123          * @param {Roo.data.Record} record The Record that was removed
21124          * @param {Number} index The index at which the record was removed
21125          */
21126         remove : true,
21127         /**
21128          * @event update
21129          * Fires when a Record has been updated
21130          * @param {Store} this
21131          * @param {Roo.data.Record} record The Record that was updated
21132          * @param {String} operation The update operation being performed.  Value may be one of:
21133          * <pre><code>
21134  Roo.data.Record.EDIT
21135  Roo.data.Record.REJECT
21136  Roo.data.Record.COMMIT
21137          * </code></pre>
21138          */
21139         update : true,
21140         /**
21141          * @event clear
21142          * Fires when the data cache has been cleared.
21143          * @param {Store} this
21144          */
21145         clear : true,
21146         /**
21147          * @event beforeload
21148          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21149          * the load action will be canceled.
21150          * @param {Store} this
21151          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21152          */
21153         beforeload : true,
21154         /**
21155          * @event beforeloadadd
21156          * Fires after a new set of Records has been loaded.
21157          * @param {Store} this
21158          * @param {Roo.data.Record[]} records The Records that were loaded
21159          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21160          */
21161         beforeloadadd : true,
21162         /**
21163          * @event load
21164          * Fires after a new set of Records has been loaded, before they are added to the store.
21165          * @param {Store} this
21166          * @param {Roo.data.Record[]} records The Records that were loaded
21167          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21168          * @params {Object} return from reader
21169          */
21170         load : true,
21171         /**
21172          * @event loadexception
21173          * Fires if an exception occurs in the Proxy during loading.
21174          * Called with the signature of the Proxy's "loadexception" event.
21175          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21176          * 
21177          * @param {Proxy} 
21178          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21179          * @param {Object} load options 
21180          * @param {Object} jsonData from your request (normally this contains the Exception)
21181          */
21182         loadexception : true
21183     });
21184     
21185     if(this.proxy){
21186         this.proxy = Roo.factory(this.proxy, Roo.data);
21187         this.proxy.xmodule = this.xmodule || false;
21188         this.relayEvents(this.proxy,  ["loadexception"]);
21189     }
21190     this.sortToggle = {};
21191     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21192
21193     Roo.data.Store.superclass.constructor.call(this);
21194
21195     if(this.inlineData){
21196         this.loadData(this.inlineData);
21197         delete this.inlineData;
21198     }
21199 };
21200
21201 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21202      /**
21203     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21204     * without a remote query - used by combo/forms at present.
21205     */
21206     
21207     /**
21208     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21209     */
21210     /**
21211     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21212     */
21213     /**
21214     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21215     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21216     */
21217     /**
21218     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21219     * on any HTTP request
21220     */
21221     /**
21222     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21223     */
21224     /**
21225     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21226     */
21227     multiSort: false,
21228     /**
21229     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21230     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21231     */
21232     remoteSort : false,
21233
21234     /**
21235     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21236      * loaded or when a record is removed. (defaults to false).
21237     */
21238     pruneModifiedRecords : false,
21239
21240     // private
21241     lastOptions : null,
21242
21243     /**
21244      * Add Records to the Store and fires the add event.
21245      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21246      */
21247     add : function(records){
21248         records = [].concat(records);
21249         for(var i = 0, len = records.length; i < len; i++){
21250             records[i].join(this);
21251         }
21252         var index = this.data.length;
21253         this.data.addAll(records);
21254         this.fireEvent("add", this, records, index);
21255     },
21256
21257     /**
21258      * Remove a Record from the Store and fires the remove event.
21259      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21260      */
21261     remove : function(record){
21262         var index = this.data.indexOf(record);
21263         this.data.removeAt(index);
21264         if(this.pruneModifiedRecords){
21265             this.modified.remove(record);
21266         }
21267         this.fireEvent("remove", this, record, index);
21268     },
21269
21270     /**
21271      * Remove all Records from the Store and fires the clear event.
21272      */
21273     removeAll : function(){
21274         this.data.clear();
21275         if(this.pruneModifiedRecords){
21276             this.modified = [];
21277         }
21278         this.fireEvent("clear", this);
21279     },
21280
21281     /**
21282      * Inserts Records to the Store at the given index and fires the add event.
21283      * @param {Number} index The start index at which to insert the passed Records.
21284      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21285      */
21286     insert : function(index, records){
21287         records = [].concat(records);
21288         for(var i = 0, len = records.length; i < len; i++){
21289             this.data.insert(index, records[i]);
21290             records[i].join(this);
21291         }
21292         this.fireEvent("add", this, records, index);
21293     },
21294
21295     /**
21296      * Get the index within the cache of the passed Record.
21297      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21298      * @return {Number} The index of the passed Record. Returns -1 if not found.
21299      */
21300     indexOf : function(record){
21301         return this.data.indexOf(record);
21302     },
21303
21304     /**
21305      * Get the index within the cache of the Record with the passed id.
21306      * @param {String} id The id of the Record to find.
21307      * @return {Number} The index of the Record. Returns -1 if not found.
21308      */
21309     indexOfId : function(id){
21310         return this.data.indexOfKey(id);
21311     },
21312
21313     /**
21314      * Get the Record with the specified id.
21315      * @param {String} id The id of the Record to find.
21316      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21317      */
21318     getById : function(id){
21319         return this.data.key(id);
21320     },
21321
21322     /**
21323      * Get the Record at the specified index.
21324      * @param {Number} index The index of the Record to find.
21325      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21326      */
21327     getAt : function(index){
21328         return this.data.itemAt(index);
21329     },
21330
21331     /**
21332      * Returns a range of Records between specified indices.
21333      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21334      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21335      * @return {Roo.data.Record[]} An array of Records
21336      */
21337     getRange : function(start, end){
21338         return this.data.getRange(start, end);
21339     },
21340
21341     // private
21342     storeOptions : function(o){
21343         o = Roo.apply({}, o);
21344         delete o.callback;
21345         delete o.scope;
21346         this.lastOptions = o;
21347     },
21348
21349     /**
21350      * Loads the Record cache from the configured Proxy using the configured Reader.
21351      * <p>
21352      * If using remote paging, then the first load call must specify the <em>start</em>
21353      * and <em>limit</em> properties in the options.params property to establish the initial
21354      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21355      * <p>
21356      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21357      * and this call will return before the new data has been loaded. Perform any post-processing
21358      * in a callback function, or in a "load" event handler.</strong>
21359      * <p>
21360      * @param {Object} options An object containing properties which control loading options:<ul>
21361      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21362      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21363      * passed the following arguments:<ul>
21364      * <li>r : Roo.data.Record[]</li>
21365      * <li>options: Options object from the load call</li>
21366      * <li>success: Boolean success indicator</li></ul></li>
21367      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21368      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21369      * </ul>
21370      */
21371     load : function(options){
21372         options = options || {};
21373         if(this.fireEvent("beforeload", this, options) !== false){
21374             this.storeOptions(options);
21375             var p = Roo.apply(options.params || {}, this.baseParams);
21376             // if meta was not loaded from remote source.. try requesting it.
21377             if (!this.reader.metaFromRemote) {
21378                 p._requestMeta = 1;
21379             }
21380             if(this.sortInfo && this.remoteSort){
21381                 var pn = this.paramNames;
21382                 p[pn["sort"]] = this.sortInfo.field;
21383                 p[pn["dir"]] = this.sortInfo.direction;
21384             }
21385             if (this.multiSort) {
21386                 var pn = this.paramNames;
21387                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21388             }
21389             
21390             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21391         }
21392     },
21393
21394     /**
21395      * Reloads the Record cache from the configured Proxy using the configured Reader and
21396      * the options from the last load operation performed.
21397      * @param {Object} options (optional) An object containing properties which may override the options
21398      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21399      * the most recently used options are reused).
21400      */
21401     reload : function(options){
21402         this.load(Roo.applyIf(options||{}, this.lastOptions));
21403     },
21404
21405     // private
21406     // Called as a callback by the Reader during a load operation.
21407     loadRecords : function(o, options, success){
21408         if(!o || success === false){
21409             if(success !== false){
21410                 this.fireEvent("load", this, [], options, o);
21411             }
21412             if(options.callback){
21413                 options.callback.call(options.scope || this, [], options, false);
21414             }
21415             return;
21416         }
21417         // if data returned failure - throw an exception.
21418         if (o.success === false) {
21419             // show a message if no listener is registered.
21420             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21421                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21422             }
21423             // loadmask wil be hooked into this..
21424             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21425             return;
21426         }
21427         var r = o.records, t = o.totalRecords || r.length;
21428         
21429         this.fireEvent("beforeloadadd", this, r, options, o);
21430         
21431         if(!options || options.add !== true){
21432             if(this.pruneModifiedRecords){
21433                 this.modified = [];
21434             }
21435             for(var i = 0, len = r.length; i < len; i++){
21436                 r[i].join(this);
21437             }
21438             if(this.snapshot){
21439                 this.data = this.snapshot;
21440                 delete this.snapshot;
21441             }
21442             this.data.clear();
21443             this.data.addAll(r);
21444             this.totalLength = t;
21445             this.applySort();
21446             this.fireEvent("datachanged", this);
21447         }else{
21448             this.totalLength = Math.max(t, this.data.length+r.length);
21449             this.add(r);
21450         }
21451         this.fireEvent("load", this, r, options, o);
21452         if(options.callback){
21453             options.callback.call(options.scope || this, r, options, true);
21454         }
21455     },
21456
21457
21458     /**
21459      * Loads data from a passed data block. A Reader which understands the format of the data
21460      * must have been configured in the constructor.
21461      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21462      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21463      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21464      */
21465     loadData : function(o, append){
21466         var r = this.reader.readRecords(o);
21467         this.loadRecords(r, {add: append}, true);
21468     },
21469
21470     /**
21471      * Gets the number of cached records.
21472      * <p>
21473      * <em>If using paging, this may not be the total size of the dataset. If the data object
21474      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21475      * the data set size</em>
21476      */
21477     getCount : function(){
21478         return this.data.length || 0;
21479     },
21480
21481     /**
21482      * Gets the total number of records in the dataset as returned by the server.
21483      * <p>
21484      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21485      * the dataset size</em>
21486      */
21487     getTotalCount : function(){
21488         return this.totalLength || 0;
21489     },
21490
21491     /**
21492      * Returns the sort state of the Store as an object with two properties:
21493      * <pre><code>
21494  field {String} The name of the field by which the Records are sorted
21495  direction {String} The sort order, "ASC" or "DESC"
21496      * </code></pre>
21497      */
21498     getSortState : function(){
21499         return this.sortInfo;
21500     },
21501
21502     // private
21503     applySort : function(){
21504         if(this.sortInfo && !this.remoteSort){
21505             var s = this.sortInfo, f = s.field;
21506             var st = this.fields.get(f).sortType;
21507             var fn = function(r1, r2){
21508                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21509                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21510             };
21511             this.data.sort(s.direction, fn);
21512             if(this.snapshot && this.snapshot != this.data){
21513                 this.snapshot.sort(s.direction, fn);
21514             }
21515         }
21516     },
21517
21518     /**
21519      * Sets the default sort column and order to be used by the next load operation.
21520      * @param {String} fieldName The name of the field to sort by.
21521      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21522      */
21523     setDefaultSort : function(field, dir){
21524         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21525     },
21526
21527     /**
21528      * Sort the Records.
21529      * If remote sorting is used, the sort is performed on the server, and the cache is
21530      * reloaded. If local sorting is used, the cache is sorted internally.
21531      * @param {String} fieldName The name of the field to sort by.
21532      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21533      */
21534     sort : function(fieldName, dir){
21535         var f = this.fields.get(fieldName);
21536         if(!dir){
21537             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21538             
21539             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21540                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21541             }else{
21542                 dir = f.sortDir;
21543             }
21544         }
21545         this.sortToggle[f.name] = dir;
21546         this.sortInfo = {field: f.name, direction: dir};
21547         if(!this.remoteSort){
21548             this.applySort();
21549             this.fireEvent("datachanged", this);
21550         }else{
21551             this.load(this.lastOptions);
21552         }
21553     },
21554
21555     /**
21556      * Calls the specified function for each of the Records in the cache.
21557      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21558      * Returning <em>false</em> aborts and exits the iteration.
21559      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21560      */
21561     each : function(fn, scope){
21562         this.data.each(fn, scope);
21563     },
21564
21565     /**
21566      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21567      * (e.g., during paging).
21568      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21569      */
21570     getModifiedRecords : function(){
21571         return this.modified;
21572     },
21573
21574     // private
21575     createFilterFn : function(property, value, anyMatch){
21576         if(!value.exec){ // not a regex
21577             value = String(value);
21578             if(value.length == 0){
21579                 return false;
21580             }
21581             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21582         }
21583         return function(r){
21584             return value.test(r.data[property]);
21585         };
21586     },
21587
21588     /**
21589      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21590      * @param {String} property A field on your records
21591      * @param {Number} start The record index to start at (defaults to 0)
21592      * @param {Number} end The last record index to include (defaults to length - 1)
21593      * @return {Number} The sum
21594      */
21595     sum : function(property, start, end){
21596         var rs = this.data.items, v = 0;
21597         start = start || 0;
21598         end = (end || end === 0) ? end : rs.length-1;
21599
21600         for(var i = start; i <= end; i++){
21601             v += (rs[i].data[property] || 0);
21602         }
21603         return v;
21604     },
21605
21606     /**
21607      * Filter the records by a specified property.
21608      * @param {String} field A field on your records
21609      * @param {String/RegExp} value Either a string that the field
21610      * should start with or a RegExp to test against the field
21611      * @param {Boolean} anyMatch True to match any part not just the beginning
21612      */
21613     filter : function(property, value, anyMatch){
21614         var fn = this.createFilterFn(property, value, anyMatch);
21615         return fn ? this.filterBy(fn) : this.clearFilter();
21616     },
21617
21618     /**
21619      * Filter by a function. The specified function will be called with each
21620      * record in this data source. If the function returns true the record is included,
21621      * otherwise it is filtered.
21622      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21623      * @param {Object} scope (optional) The scope of the function (defaults to this)
21624      */
21625     filterBy : function(fn, scope){
21626         this.snapshot = this.snapshot || this.data;
21627         this.data = this.queryBy(fn, scope||this);
21628         this.fireEvent("datachanged", this);
21629     },
21630
21631     /**
21632      * Query the records by a specified property.
21633      * @param {String} field A field on your records
21634      * @param {String/RegExp} value Either a string that the field
21635      * should start with or a RegExp to test against the field
21636      * @param {Boolean} anyMatch True to match any part not just the beginning
21637      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21638      */
21639     query : function(property, value, anyMatch){
21640         var fn = this.createFilterFn(property, value, anyMatch);
21641         return fn ? this.queryBy(fn) : this.data.clone();
21642     },
21643
21644     /**
21645      * Query by a function. The specified function will be called with each
21646      * record in this data source. If the function returns true the record is included
21647      * in the results.
21648      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21649      * @param {Object} scope (optional) The scope of the function (defaults to this)
21650       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21651      **/
21652     queryBy : function(fn, scope){
21653         var data = this.snapshot || this.data;
21654         return data.filterBy(fn, scope||this);
21655     },
21656
21657     /**
21658      * Collects unique values for a particular dataIndex from this store.
21659      * @param {String} dataIndex The property to collect
21660      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21661      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21662      * @return {Array} An array of the unique values
21663      **/
21664     collect : function(dataIndex, allowNull, bypassFilter){
21665         var d = (bypassFilter === true && this.snapshot) ?
21666                 this.snapshot.items : this.data.items;
21667         var v, sv, r = [], l = {};
21668         for(var i = 0, len = d.length; i < len; i++){
21669             v = d[i].data[dataIndex];
21670             sv = String(v);
21671             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21672                 l[sv] = true;
21673                 r[r.length] = v;
21674             }
21675         }
21676         return r;
21677     },
21678
21679     /**
21680      * Revert to a view of the Record cache with no filtering applied.
21681      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21682      */
21683     clearFilter : function(suppressEvent){
21684         if(this.snapshot && this.snapshot != this.data){
21685             this.data = this.snapshot;
21686             delete this.snapshot;
21687             if(suppressEvent !== true){
21688                 this.fireEvent("datachanged", this);
21689             }
21690         }
21691     },
21692
21693     // private
21694     afterEdit : function(record){
21695         if(this.modified.indexOf(record) == -1){
21696             this.modified.push(record);
21697         }
21698         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21699     },
21700     
21701     // private
21702     afterReject : function(record){
21703         this.modified.remove(record);
21704         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21705     },
21706
21707     // private
21708     afterCommit : function(record){
21709         this.modified.remove(record);
21710         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21711     },
21712
21713     /**
21714      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21715      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21716      */
21717     commitChanges : function(){
21718         var m = this.modified.slice(0);
21719         this.modified = [];
21720         for(var i = 0, len = m.length; i < len; i++){
21721             m[i].commit();
21722         }
21723     },
21724
21725     /**
21726      * Cancel outstanding changes on all changed records.
21727      */
21728     rejectChanges : function(){
21729         var m = this.modified.slice(0);
21730         this.modified = [];
21731         for(var i = 0, len = m.length; i < len; i++){
21732             m[i].reject();
21733         }
21734     },
21735
21736     onMetaChange : function(meta, rtype, o){
21737         this.recordType = rtype;
21738         this.fields = rtype.prototype.fields;
21739         delete this.snapshot;
21740         this.sortInfo = meta.sortInfo || this.sortInfo;
21741         this.modified = [];
21742         this.fireEvent('metachange', this, this.reader.meta);
21743     },
21744     
21745     moveIndex : function(data, type)
21746     {
21747         var index = this.indexOf(data);
21748         
21749         var newIndex = index + type;
21750         
21751         this.remove(data);
21752         
21753         this.insert(newIndex, data);
21754         
21755     }
21756 });/*
21757  * Based on:
21758  * Ext JS Library 1.1.1
21759  * Copyright(c) 2006-2007, Ext JS, LLC.
21760  *
21761  * Originally Released Under LGPL - original licence link has changed is not relivant.
21762  *
21763  * Fork - LGPL
21764  * <script type="text/javascript">
21765  */
21766
21767 /**
21768  * @class Roo.data.SimpleStore
21769  * @extends Roo.data.Store
21770  * Small helper class to make creating Stores from Array data easier.
21771  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21772  * @cfg {Array} fields An array of field definition objects, or field name strings.
21773  * @cfg {Array} data The multi-dimensional array of data
21774  * @constructor
21775  * @param {Object} config
21776  */
21777 Roo.data.SimpleStore = function(config){
21778     Roo.data.SimpleStore.superclass.constructor.call(this, {
21779         isLocal : true,
21780         reader: new Roo.data.ArrayReader({
21781                 id: config.id
21782             },
21783             Roo.data.Record.create(config.fields)
21784         ),
21785         proxy : new Roo.data.MemoryProxy(config.data)
21786     });
21787     this.load();
21788 };
21789 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21790  * Based on:
21791  * Ext JS Library 1.1.1
21792  * Copyright(c) 2006-2007, Ext JS, LLC.
21793  *
21794  * Originally Released Under LGPL - original licence link has changed is not relivant.
21795  *
21796  * Fork - LGPL
21797  * <script type="text/javascript">
21798  */
21799
21800 /**
21801 /**
21802  * @extends Roo.data.Store
21803  * @class Roo.data.JsonStore
21804  * Small helper class to make creating Stores for JSON data easier. <br/>
21805 <pre><code>
21806 var store = new Roo.data.JsonStore({
21807     url: 'get-images.php',
21808     root: 'images',
21809     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21810 });
21811 </code></pre>
21812  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21813  * JsonReader and HttpProxy (unless inline data is provided).</b>
21814  * @cfg {Array} fields An array of field definition objects, or field name strings.
21815  * @constructor
21816  * @param {Object} config
21817  */
21818 Roo.data.JsonStore = function(c){
21819     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21820         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21821         reader: new Roo.data.JsonReader(c, c.fields)
21822     }));
21823 };
21824 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21825  * Based on:
21826  * Ext JS Library 1.1.1
21827  * Copyright(c) 2006-2007, Ext JS, LLC.
21828  *
21829  * Originally Released Under LGPL - original licence link has changed is not relivant.
21830  *
21831  * Fork - LGPL
21832  * <script type="text/javascript">
21833  */
21834
21835  
21836 Roo.data.Field = function(config){
21837     if(typeof config == "string"){
21838         config = {name: config};
21839     }
21840     Roo.apply(this, config);
21841     
21842     if(!this.type){
21843         this.type = "auto";
21844     }
21845     
21846     var st = Roo.data.SortTypes;
21847     // named sortTypes are supported, here we look them up
21848     if(typeof this.sortType == "string"){
21849         this.sortType = st[this.sortType];
21850     }
21851     
21852     // set default sortType for strings and dates
21853     if(!this.sortType){
21854         switch(this.type){
21855             case "string":
21856                 this.sortType = st.asUCString;
21857                 break;
21858             case "date":
21859                 this.sortType = st.asDate;
21860                 break;
21861             default:
21862                 this.sortType = st.none;
21863         }
21864     }
21865
21866     // define once
21867     var stripRe = /[\$,%]/g;
21868
21869     // prebuilt conversion function for this field, instead of
21870     // switching every time we're reading a value
21871     if(!this.convert){
21872         var cv, dateFormat = this.dateFormat;
21873         switch(this.type){
21874             case "":
21875             case "auto":
21876             case undefined:
21877                 cv = function(v){ return v; };
21878                 break;
21879             case "string":
21880                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21881                 break;
21882             case "int":
21883                 cv = function(v){
21884                     return v !== undefined && v !== null && v !== '' ?
21885                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21886                     };
21887                 break;
21888             case "float":
21889                 cv = function(v){
21890                     return v !== undefined && v !== null && v !== '' ?
21891                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21892                     };
21893                 break;
21894             case "bool":
21895             case "boolean":
21896                 cv = function(v){ return v === true || v === "true" || v == 1; };
21897                 break;
21898             case "date":
21899                 cv = function(v){
21900                     if(!v){
21901                         return '';
21902                     }
21903                     if(v instanceof Date){
21904                         return v;
21905                     }
21906                     if(dateFormat){
21907                         if(dateFormat == "timestamp"){
21908                             return new Date(v*1000);
21909                         }
21910                         return Date.parseDate(v, dateFormat);
21911                     }
21912                     var parsed = Date.parse(v);
21913                     return parsed ? new Date(parsed) : null;
21914                 };
21915              break;
21916             
21917         }
21918         this.convert = cv;
21919     }
21920 };
21921
21922 Roo.data.Field.prototype = {
21923     dateFormat: null,
21924     defaultValue: "",
21925     mapping: null,
21926     sortType : null,
21927     sortDir : "ASC"
21928 };/*
21929  * Based on:
21930  * Ext JS Library 1.1.1
21931  * Copyright(c) 2006-2007, Ext JS, LLC.
21932  *
21933  * Originally Released Under LGPL - original licence link has changed is not relivant.
21934  *
21935  * Fork - LGPL
21936  * <script type="text/javascript">
21937  */
21938  
21939 // Base class for reading structured data from a data source.  This class is intended to be
21940 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21941
21942 /**
21943  * @class Roo.data.DataReader
21944  * Base class for reading structured data from a data source.  This class is intended to be
21945  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21946  */
21947
21948 Roo.data.DataReader = function(meta, recordType){
21949     
21950     this.meta = meta;
21951     
21952     this.recordType = recordType instanceof Array ? 
21953         Roo.data.Record.create(recordType) : recordType;
21954 };
21955
21956 Roo.data.DataReader.prototype = {
21957      /**
21958      * Create an empty record
21959      * @param {Object} data (optional) - overlay some values
21960      * @return {Roo.data.Record} record created.
21961      */
21962     newRow :  function(d) {
21963         var da =  {};
21964         this.recordType.prototype.fields.each(function(c) {
21965             switch( c.type) {
21966                 case 'int' : da[c.name] = 0; break;
21967                 case 'date' : da[c.name] = new Date(); break;
21968                 case 'float' : da[c.name] = 0.0; break;
21969                 case 'boolean' : da[c.name] = false; break;
21970                 default : da[c.name] = ""; break;
21971             }
21972             
21973         });
21974         return new this.recordType(Roo.apply(da, d));
21975     }
21976     
21977 };/*
21978  * Based on:
21979  * Ext JS Library 1.1.1
21980  * Copyright(c) 2006-2007, Ext JS, LLC.
21981  *
21982  * Originally Released Under LGPL - original licence link has changed is not relivant.
21983  *
21984  * Fork - LGPL
21985  * <script type="text/javascript">
21986  */
21987
21988 /**
21989  * @class Roo.data.DataProxy
21990  * @extends Roo.data.Observable
21991  * This class is an abstract base class for implementations which provide retrieval of
21992  * unformatted data objects.<br>
21993  * <p>
21994  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21995  * (of the appropriate type which knows how to parse the data object) to provide a block of
21996  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21997  * <p>
21998  * Custom implementations must implement the load method as described in
21999  * {@link Roo.data.HttpProxy#load}.
22000  */
22001 Roo.data.DataProxy = function(){
22002     this.addEvents({
22003         /**
22004          * @event beforeload
22005          * Fires before a network request is made to retrieve a data object.
22006          * @param {Object} This DataProxy object.
22007          * @param {Object} params The params parameter to the load function.
22008          */
22009         beforeload : true,
22010         /**
22011          * @event load
22012          * Fires before the load method's callback is called.
22013          * @param {Object} This DataProxy object.
22014          * @param {Object} o The data object.
22015          * @param {Object} arg The callback argument object passed to the load function.
22016          */
22017         load : true,
22018         /**
22019          * @event loadexception
22020          * Fires if an Exception occurs during data retrieval.
22021          * @param {Object} This DataProxy object.
22022          * @param {Object} o The data object.
22023          * @param {Object} arg The callback argument object passed to the load function.
22024          * @param {Object} e The Exception.
22025          */
22026         loadexception : true
22027     });
22028     Roo.data.DataProxy.superclass.constructor.call(this);
22029 };
22030
22031 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22032
22033     /**
22034      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22035      */
22036 /*
22037  * Based on:
22038  * Ext JS Library 1.1.1
22039  * Copyright(c) 2006-2007, Ext JS, LLC.
22040  *
22041  * Originally Released Under LGPL - original licence link has changed is not relivant.
22042  *
22043  * Fork - LGPL
22044  * <script type="text/javascript">
22045  */
22046 /**
22047  * @class Roo.data.MemoryProxy
22048  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22049  * to the Reader when its load method is called.
22050  * @constructor
22051  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22052  */
22053 Roo.data.MemoryProxy = function(data){
22054     if (data.data) {
22055         data = data.data;
22056     }
22057     Roo.data.MemoryProxy.superclass.constructor.call(this);
22058     this.data = data;
22059 };
22060
22061 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22062     /**
22063      * Load data from the requested source (in this case an in-memory
22064      * data object passed to the constructor), read the data object into
22065      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22066      * process that block using the passed callback.
22067      * @param {Object} params This parameter is not used by the MemoryProxy class.
22068      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22069      * object into a block of Roo.data.Records.
22070      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22071      * The function must be passed <ul>
22072      * <li>The Record block object</li>
22073      * <li>The "arg" argument from the load function</li>
22074      * <li>A boolean success indicator</li>
22075      * </ul>
22076      * @param {Object} scope The scope in which to call the callback
22077      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22078      */
22079     load : function(params, reader, callback, scope, arg){
22080         params = params || {};
22081         var result;
22082         try {
22083             result = reader.readRecords(this.data);
22084         }catch(e){
22085             this.fireEvent("loadexception", this, arg, null, e);
22086             callback.call(scope, null, arg, false);
22087             return;
22088         }
22089         callback.call(scope, result, arg, true);
22090     },
22091     
22092     // private
22093     update : function(params, records){
22094         
22095     }
22096 });/*
22097  * Based on:
22098  * Ext JS Library 1.1.1
22099  * Copyright(c) 2006-2007, Ext JS, LLC.
22100  *
22101  * Originally Released Under LGPL - original licence link has changed is not relivant.
22102  *
22103  * Fork - LGPL
22104  * <script type="text/javascript">
22105  */
22106 /**
22107  * @class Roo.data.HttpProxy
22108  * @extends Roo.data.DataProxy
22109  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22110  * configured to reference a certain URL.<br><br>
22111  * <p>
22112  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22113  * from which the running page was served.<br><br>
22114  * <p>
22115  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22116  * <p>
22117  * Be aware that to enable the browser to parse an XML document, the server must set
22118  * the Content-Type header in the HTTP response to "text/xml".
22119  * @constructor
22120  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22121  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22122  * will be used to make the request.
22123  */
22124 Roo.data.HttpProxy = function(conn){
22125     Roo.data.HttpProxy.superclass.constructor.call(this);
22126     // is conn a conn config or a real conn?
22127     this.conn = conn;
22128     this.useAjax = !conn || !conn.events;
22129   
22130 };
22131
22132 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22133     // thse are take from connection...
22134     
22135     /**
22136      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22137      */
22138     /**
22139      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22140      * extra parameters to each request made by this object. (defaults to undefined)
22141      */
22142     /**
22143      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22144      *  to each request made by this object. (defaults to undefined)
22145      */
22146     /**
22147      * @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)
22148      */
22149     /**
22150      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22151      */
22152      /**
22153      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22154      * @type Boolean
22155      */
22156   
22157
22158     /**
22159      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22160      * @type Boolean
22161      */
22162     /**
22163      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22164      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22165      * a finer-grained basis than the DataProxy events.
22166      */
22167     getConnection : function(){
22168         return this.useAjax ? Roo.Ajax : this.conn;
22169     },
22170
22171     /**
22172      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22173      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22174      * process that block using the passed callback.
22175      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22176      * for the request to the remote server.
22177      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22178      * object into a block of Roo.data.Records.
22179      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22180      * The function must be passed <ul>
22181      * <li>The Record block object</li>
22182      * <li>The "arg" argument from the load function</li>
22183      * <li>A boolean success indicator</li>
22184      * </ul>
22185      * @param {Object} scope The scope in which to call the callback
22186      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22187      */
22188     load : function(params, reader, callback, scope, arg){
22189         if(this.fireEvent("beforeload", this, params) !== false){
22190             var  o = {
22191                 params : params || {},
22192                 request: {
22193                     callback : callback,
22194                     scope : scope,
22195                     arg : arg
22196                 },
22197                 reader: reader,
22198                 callback : this.loadResponse,
22199                 scope: this
22200             };
22201             if(this.useAjax){
22202                 Roo.applyIf(o, this.conn);
22203                 if(this.activeRequest){
22204                     Roo.Ajax.abort(this.activeRequest);
22205                 }
22206                 this.activeRequest = Roo.Ajax.request(o);
22207             }else{
22208                 this.conn.request(o);
22209             }
22210         }else{
22211             callback.call(scope||this, null, arg, false);
22212         }
22213     },
22214
22215     // private
22216     loadResponse : function(o, success, response){
22217         delete this.activeRequest;
22218         if(!success){
22219             this.fireEvent("loadexception", this, o, response);
22220             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22221             return;
22222         }
22223         var result;
22224         try {
22225             result = o.reader.read(response);
22226         }catch(e){
22227             this.fireEvent("loadexception", this, o, response, e);
22228             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22229             return;
22230         }
22231         
22232         this.fireEvent("load", this, o, o.request.arg);
22233         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22234     },
22235
22236     // private
22237     update : function(dataSet){
22238
22239     },
22240
22241     // private
22242     updateResponse : function(dataSet){
22243
22244     }
22245 });/*
22246  * Based on:
22247  * Ext JS Library 1.1.1
22248  * Copyright(c) 2006-2007, Ext JS, LLC.
22249  *
22250  * Originally Released Under LGPL - original licence link has changed is not relivant.
22251  *
22252  * Fork - LGPL
22253  * <script type="text/javascript">
22254  */
22255
22256 /**
22257  * @class Roo.data.ScriptTagProxy
22258  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22259  * other than the originating domain of the running page.<br><br>
22260  * <p>
22261  * <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
22262  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22263  * <p>
22264  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22265  * source code that is used as the source inside a &lt;script> tag.<br><br>
22266  * <p>
22267  * In order for the browser to process the returned data, the server must wrap the data object
22268  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22269  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22270  * depending on whether the callback name was passed:
22271  * <p>
22272  * <pre><code>
22273 boolean scriptTag = false;
22274 String cb = request.getParameter("callback");
22275 if (cb != null) {
22276     scriptTag = true;
22277     response.setContentType("text/javascript");
22278 } else {
22279     response.setContentType("application/x-json");
22280 }
22281 Writer out = response.getWriter();
22282 if (scriptTag) {
22283     out.write(cb + "(");
22284 }
22285 out.print(dataBlock.toJsonString());
22286 if (scriptTag) {
22287     out.write(");");
22288 }
22289 </pre></code>
22290  *
22291  * @constructor
22292  * @param {Object} config A configuration object.
22293  */
22294 Roo.data.ScriptTagProxy = function(config){
22295     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22296     Roo.apply(this, config);
22297     this.head = document.getElementsByTagName("head")[0];
22298 };
22299
22300 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22301
22302 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22303     /**
22304      * @cfg {String} url The URL from which to request the data object.
22305      */
22306     /**
22307      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22308      */
22309     timeout : 30000,
22310     /**
22311      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22312      * the server the name of the callback function set up by the load call to process the returned data object.
22313      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22314      * javascript output which calls this named function passing the data object as its only parameter.
22315      */
22316     callbackParam : "callback",
22317     /**
22318      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22319      * name to the request.
22320      */
22321     nocache : true,
22322
22323     /**
22324      * Load data from the configured URL, read the data object into
22325      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22326      * process that block using the passed callback.
22327      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22328      * for the request to the remote server.
22329      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22330      * object into a block of Roo.data.Records.
22331      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22332      * The function must be passed <ul>
22333      * <li>The Record block object</li>
22334      * <li>The "arg" argument from the load function</li>
22335      * <li>A boolean success indicator</li>
22336      * </ul>
22337      * @param {Object} scope The scope in which to call the callback
22338      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22339      */
22340     load : function(params, reader, callback, scope, arg){
22341         if(this.fireEvent("beforeload", this, params) !== false){
22342
22343             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22344
22345             var url = this.url;
22346             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22347             if(this.nocache){
22348                 url += "&_dc=" + (new Date().getTime());
22349             }
22350             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22351             var trans = {
22352                 id : transId,
22353                 cb : "stcCallback"+transId,
22354                 scriptId : "stcScript"+transId,
22355                 params : params,
22356                 arg : arg,
22357                 url : url,
22358                 callback : callback,
22359                 scope : scope,
22360                 reader : reader
22361             };
22362             var conn = this;
22363
22364             window[trans.cb] = function(o){
22365                 conn.handleResponse(o, trans);
22366             };
22367
22368             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22369
22370             if(this.autoAbort !== false){
22371                 this.abort();
22372             }
22373
22374             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22375
22376             var script = document.createElement("script");
22377             script.setAttribute("src", url);
22378             script.setAttribute("type", "text/javascript");
22379             script.setAttribute("id", trans.scriptId);
22380             this.head.appendChild(script);
22381
22382             this.trans = trans;
22383         }else{
22384             callback.call(scope||this, null, arg, false);
22385         }
22386     },
22387
22388     // private
22389     isLoading : function(){
22390         return this.trans ? true : false;
22391     },
22392
22393     /**
22394      * Abort the current server request.
22395      */
22396     abort : function(){
22397         if(this.isLoading()){
22398             this.destroyTrans(this.trans);
22399         }
22400     },
22401
22402     // private
22403     destroyTrans : function(trans, isLoaded){
22404         this.head.removeChild(document.getElementById(trans.scriptId));
22405         clearTimeout(trans.timeoutId);
22406         if(isLoaded){
22407             window[trans.cb] = undefined;
22408             try{
22409                 delete window[trans.cb];
22410             }catch(e){}
22411         }else{
22412             // if hasn't been loaded, wait for load to remove it to prevent script error
22413             window[trans.cb] = function(){
22414                 window[trans.cb] = undefined;
22415                 try{
22416                     delete window[trans.cb];
22417                 }catch(e){}
22418             };
22419         }
22420     },
22421
22422     // private
22423     handleResponse : function(o, trans){
22424         this.trans = false;
22425         this.destroyTrans(trans, true);
22426         var result;
22427         try {
22428             result = trans.reader.readRecords(o);
22429         }catch(e){
22430             this.fireEvent("loadexception", this, o, trans.arg, e);
22431             trans.callback.call(trans.scope||window, null, trans.arg, false);
22432             return;
22433         }
22434         this.fireEvent("load", this, o, trans.arg);
22435         trans.callback.call(trans.scope||window, result, trans.arg, true);
22436     },
22437
22438     // private
22439     handleFailure : function(trans){
22440         this.trans = false;
22441         this.destroyTrans(trans, false);
22442         this.fireEvent("loadexception", this, null, trans.arg);
22443         trans.callback.call(trans.scope||window, null, trans.arg, false);
22444     }
22445 });/*
22446  * Based on:
22447  * Ext JS Library 1.1.1
22448  * Copyright(c) 2006-2007, Ext JS, LLC.
22449  *
22450  * Originally Released Under LGPL - original licence link has changed is not relivant.
22451  *
22452  * Fork - LGPL
22453  * <script type="text/javascript">
22454  */
22455
22456 /**
22457  * @class Roo.data.JsonReader
22458  * @extends Roo.data.DataReader
22459  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22460  * based on mappings in a provided Roo.data.Record constructor.
22461  * 
22462  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22463  * in the reply previously. 
22464  * 
22465  * <p>
22466  * Example code:
22467  * <pre><code>
22468 var RecordDef = Roo.data.Record.create([
22469     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22470     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22471 ]);
22472 var myReader = new Roo.data.JsonReader({
22473     totalProperty: "results",    // The property which contains the total dataset size (optional)
22474     root: "rows",                // The property which contains an Array of row objects
22475     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22476 }, RecordDef);
22477 </code></pre>
22478  * <p>
22479  * This would consume a JSON file like this:
22480  * <pre><code>
22481 { 'results': 2, 'rows': [
22482     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22483     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22484 }
22485 </code></pre>
22486  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22487  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22488  * paged from the remote server.
22489  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22490  * @cfg {String} root name of the property which contains the Array of row objects.
22491  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22492  * @constructor
22493  * Create a new JsonReader
22494  * @param {Object} meta Metadata configuration options
22495  * @param {Object} recordType Either an Array of field definition objects,
22496  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22497  */
22498 Roo.data.JsonReader = function(meta, recordType){
22499     
22500     meta = meta || {};
22501     // set some defaults:
22502     Roo.applyIf(meta, {
22503         totalProperty: 'total',
22504         successProperty : 'success',
22505         root : 'data',
22506         id : 'id'
22507     });
22508     
22509     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22510 };
22511 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22512     
22513     /**
22514      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22515      * Used by Store query builder to append _requestMeta to params.
22516      * 
22517      */
22518     metaFromRemote : false,
22519     /**
22520      * This method is only used by a DataProxy which has retrieved data from a remote server.
22521      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22522      * @return {Object} data A data block which is used by an Roo.data.Store object as
22523      * a cache of Roo.data.Records.
22524      */
22525     read : function(response){
22526         var json = response.responseText;
22527        
22528         var o = /* eval:var:o */ eval("("+json+")");
22529         if(!o) {
22530             throw {message: "JsonReader.read: Json object not found"};
22531         }
22532         
22533         if(o.metaData){
22534             
22535             delete this.ef;
22536             this.metaFromRemote = true;
22537             this.meta = o.metaData;
22538             this.recordType = Roo.data.Record.create(o.metaData.fields);
22539             this.onMetaChange(this.meta, this.recordType, o);
22540         }
22541         return this.readRecords(o);
22542     },
22543
22544     // private function a store will implement
22545     onMetaChange : function(meta, recordType, o){
22546
22547     },
22548
22549     /**
22550          * @ignore
22551          */
22552     simpleAccess: function(obj, subsc) {
22553         return obj[subsc];
22554     },
22555
22556         /**
22557          * @ignore
22558          */
22559     getJsonAccessor: function(){
22560         var re = /[\[\.]/;
22561         return function(expr) {
22562             try {
22563                 return(re.test(expr))
22564                     ? new Function("obj", "return obj." + expr)
22565                     : function(obj){
22566                         return obj[expr];
22567                     };
22568             } catch(e){}
22569             return Roo.emptyFn;
22570         };
22571     }(),
22572
22573     /**
22574      * Create a data block containing Roo.data.Records from an XML document.
22575      * @param {Object} o An object which contains an Array of row objects in the property specified
22576      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22577      * which contains the total size of the dataset.
22578      * @return {Object} data A data block which is used by an Roo.data.Store object as
22579      * a cache of Roo.data.Records.
22580      */
22581     readRecords : function(o){
22582         /**
22583          * After any data loads, the raw JSON data is available for further custom processing.
22584          * @type Object
22585          */
22586         this.o = o;
22587         var s = this.meta, Record = this.recordType,
22588             f = Record.prototype.fields, fi = f.items, fl = f.length;
22589
22590 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22591         if (!this.ef) {
22592             if(s.totalProperty) {
22593                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22594                 }
22595                 if(s.successProperty) {
22596                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22597                 }
22598                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22599                 if (s.id) {
22600                         var g = this.getJsonAccessor(s.id);
22601                         this.getId = function(rec) {
22602                                 var r = g(rec);
22603                                 return (r === undefined || r === "") ? null : r;
22604                         };
22605                 } else {
22606                         this.getId = function(){return null;};
22607                 }
22608             this.ef = [];
22609             for(var jj = 0; jj < fl; jj++){
22610                 f = fi[jj];
22611                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22612                 this.ef[jj] = this.getJsonAccessor(map);
22613             }
22614         }
22615
22616         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22617         if(s.totalProperty){
22618             var vt = parseInt(this.getTotal(o), 10);
22619             if(!isNaN(vt)){
22620                 totalRecords = vt;
22621             }
22622         }
22623         if(s.successProperty){
22624             var vs = this.getSuccess(o);
22625             if(vs === false || vs === 'false'){
22626                 success = false;
22627             }
22628         }
22629         var records = [];
22630             for(var i = 0; i < c; i++){
22631                     var n = root[i];
22632                 var values = {};
22633                 var id = this.getId(n);
22634                 for(var j = 0; j < fl; j++){
22635                     f = fi[j];
22636                 var v = this.ef[j](n);
22637                 if (!f.convert) {
22638                     Roo.log('missing convert for ' + f.name);
22639                     Roo.log(f);
22640                     continue;
22641                 }
22642                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22643                 }
22644                 var record = new Record(values, id);
22645                 record.json = n;
22646                 records[i] = record;
22647             }
22648             return {
22649             raw : o,
22650                 success : success,
22651                 records : records,
22652                 totalRecords : totalRecords
22653             };
22654     }
22655 });/*
22656  * Based on:
22657  * Ext JS Library 1.1.1
22658  * Copyright(c) 2006-2007, Ext JS, LLC.
22659  *
22660  * Originally Released Under LGPL - original licence link has changed is not relivant.
22661  *
22662  * Fork - LGPL
22663  * <script type="text/javascript">
22664  */
22665
22666 /**
22667  * @class Roo.data.XmlReader
22668  * @extends Roo.data.DataReader
22669  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22670  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22671  * <p>
22672  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22673  * header in the HTTP response must be set to "text/xml".</em>
22674  * <p>
22675  * Example code:
22676  * <pre><code>
22677 var RecordDef = Roo.data.Record.create([
22678    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22679    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22680 ]);
22681 var myReader = new Roo.data.XmlReader({
22682    totalRecords: "results", // The element which contains the total dataset size (optional)
22683    record: "row",           // The repeated element which contains row information
22684    id: "id"                 // The element within the row that provides an ID for the record (optional)
22685 }, RecordDef);
22686 </code></pre>
22687  * <p>
22688  * This would consume an XML file like this:
22689  * <pre><code>
22690 &lt;?xml?>
22691 &lt;dataset>
22692  &lt;results>2&lt;/results>
22693  &lt;row>
22694    &lt;id>1&lt;/id>
22695    &lt;name>Bill&lt;/name>
22696    &lt;occupation>Gardener&lt;/occupation>
22697  &lt;/row>
22698  &lt;row>
22699    &lt;id>2&lt;/id>
22700    &lt;name>Ben&lt;/name>
22701    &lt;occupation>Horticulturalist&lt;/occupation>
22702  &lt;/row>
22703 &lt;/dataset>
22704 </code></pre>
22705  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22706  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22707  * paged from the remote server.
22708  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22709  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22710  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22711  * a record identifier value.
22712  * @constructor
22713  * Create a new XmlReader
22714  * @param {Object} meta Metadata configuration options
22715  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22716  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22717  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22718  */
22719 Roo.data.XmlReader = function(meta, recordType){
22720     meta = meta || {};
22721     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22722 };
22723 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22724     /**
22725      * This method is only used by a DataProxy which has retrieved data from a remote server.
22726          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22727          * to contain a method called 'responseXML' that returns an XML document object.
22728      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22729      * a cache of Roo.data.Records.
22730      */
22731     read : function(response){
22732         var doc = response.responseXML;
22733         if(!doc) {
22734             throw {message: "XmlReader.read: XML Document not available"};
22735         }
22736         return this.readRecords(doc);
22737     },
22738
22739     /**
22740      * Create a data block containing Roo.data.Records from an XML document.
22741          * @param {Object} doc A parsed XML document.
22742      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22743      * a cache of Roo.data.Records.
22744      */
22745     readRecords : function(doc){
22746         /**
22747          * After any data loads/reads, the raw XML Document is available for further custom processing.
22748          * @type XMLDocument
22749          */
22750         this.xmlData = doc;
22751         var root = doc.documentElement || doc;
22752         var q = Roo.DomQuery;
22753         var recordType = this.recordType, fields = recordType.prototype.fields;
22754         var sid = this.meta.id;
22755         var totalRecords = 0, success = true;
22756         if(this.meta.totalRecords){
22757             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22758         }
22759         
22760         if(this.meta.success){
22761             var sv = q.selectValue(this.meta.success, root, true);
22762             success = sv !== false && sv !== 'false';
22763         }
22764         var records = [];
22765         var ns = q.select(this.meta.record, root);
22766         for(var i = 0, len = ns.length; i < len; i++) {
22767                 var n = ns[i];
22768                 var values = {};
22769                 var id = sid ? q.selectValue(sid, n) : undefined;
22770                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22771                     var f = fields.items[j];
22772                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22773                     v = f.convert(v);
22774                     values[f.name] = v;
22775                 }
22776                 var record = new recordType(values, id);
22777                 record.node = n;
22778                 records[records.length] = record;
22779             }
22780
22781             return {
22782                 success : success,
22783                 records : records,
22784                 totalRecords : totalRecords || records.length
22785             };
22786     }
22787 });/*
22788  * Based on:
22789  * Ext JS Library 1.1.1
22790  * Copyright(c) 2006-2007, Ext JS, LLC.
22791  *
22792  * Originally Released Under LGPL - original licence link has changed is not relivant.
22793  *
22794  * Fork - LGPL
22795  * <script type="text/javascript">
22796  */
22797
22798 /**
22799  * @class Roo.data.ArrayReader
22800  * @extends Roo.data.DataReader
22801  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22802  * Each element of that Array represents a row of data fields. The
22803  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22804  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22805  * <p>
22806  * Example code:.
22807  * <pre><code>
22808 var RecordDef = Roo.data.Record.create([
22809     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22810     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22811 ]);
22812 var myReader = new Roo.data.ArrayReader({
22813     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22814 }, RecordDef);
22815 </code></pre>
22816  * <p>
22817  * This would consume an Array like this:
22818  * <pre><code>
22819 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22820   </code></pre>
22821  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22822  * @constructor
22823  * Create a new JsonReader
22824  * @param {Object} meta Metadata configuration options.
22825  * @param {Object} recordType Either an Array of field definition objects
22826  * as specified to {@link Roo.data.Record#create},
22827  * or an {@link Roo.data.Record} object
22828  * created using {@link Roo.data.Record#create}.
22829  */
22830 Roo.data.ArrayReader = function(meta, recordType){
22831     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22832 };
22833
22834 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22835     /**
22836      * Create a data block containing Roo.data.Records from an XML document.
22837      * @param {Object} o An Array of row objects which represents the dataset.
22838      * @return {Object} data A data block which is used by an Roo.data.Store object as
22839      * a cache of Roo.data.Records.
22840      */
22841     readRecords : function(o){
22842         var sid = this.meta ? this.meta.id : null;
22843         var recordType = this.recordType, fields = recordType.prototype.fields;
22844         var records = [];
22845         var root = o;
22846             for(var i = 0; i < root.length; i++){
22847                     var n = root[i];
22848                 var values = {};
22849                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22850                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22851                 var f = fields.items[j];
22852                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22853                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22854                 v = f.convert(v);
22855                 values[f.name] = v;
22856             }
22857                 var record = new recordType(values, id);
22858                 record.json = n;
22859                 records[records.length] = record;
22860             }
22861             return {
22862                 records : records,
22863                 totalRecords : records.length
22864             };
22865     }
22866 });/*
22867  * Based on:
22868  * Ext JS Library 1.1.1
22869  * Copyright(c) 2006-2007, Ext JS, LLC.
22870  *
22871  * Originally Released Under LGPL - original licence link has changed is not relivant.
22872  *
22873  * Fork - LGPL
22874  * <script type="text/javascript">
22875  */
22876
22877
22878 /**
22879  * @class Roo.data.Tree
22880  * @extends Roo.util.Observable
22881  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22882  * in the tree have most standard DOM functionality.
22883  * @constructor
22884  * @param {Node} root (optional) The root node
22885  */
22886 Roo.data.Tree = function(root){
22887    this.nodeHash = {};
22888    /**
22889     * The root node for this tree
22890     * @type Node
22891     */
22892    this.root = null;
22893    if(root){
22894        this.setRootNode(root);
22895    }
22896    this.addEvents({
22897        /**
22898         * @event append
22899         * Fires when a new child node is appended to a node in this tree.
22900         * @param {Tree} tree The owner tree
22901         * @param {Node} parent The parent node
22902         * @param {Node} node The newly appended node
22903         * @param {Number} index The index of the newly appended node
22904         */
22905        "append" : true,
22906        /**
22907         * @event remove
22908         * Fires when a child node is removed from 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 removed
22912         */
22913        "remove" : true,
22914        /**
22915         * @event move
22916         * Fires when a node is moved to a new location in the tree
22917         * @param {Tree} tree The owner tree
22918         * @param {Node} node The node moved
22919         * @param {Node} oldParent The old parent of this node
22920         * @param {Node} newParent The new parent of this node
22921         * @param {Number} index The index it was moved to
22922         */
22923        "move" : true,
22924        /**
22925         * @event insert
22926         * Fires when a new child node is inserted in a node in this tree.
22927         * @param {Tree} tree The owner tree
22928         * @param {Node} parent The parent node
22929         * @param {Node} node The child node inserted
22930         * @param {Node} refNode The child node the node was inserted before
22931         */
22932        "insert" : true,
22933        /**
22934         * @event beforeappend
22935         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22936         * @param {Tree} tree The owner tree
22937         * @param {Node} parent The parent node
22938         * @param {Node} node The child node to be appended
22939         */
22940        "beforeappend" : true,
22941        /**
22942         * @event beforeremove
22943         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22944         * @param {Tree} tree The owner tree
22945         * @param {Node} parent The parent node
22946         * @param {Node} node The child node to be removed
22947         */
22948        "beforeremove" : true,
22949        /**
22950         * @event beforemove
22951         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22952         * @param {Tree} tree The owner tree
22953         * @param {Node} node The node being moved
22954         * @param {Node} oldParent The parent of the node
22955         * @param {Node} newParent The new parent the node is moving to
22956         * @param {Number} index The index it is being moved to
22957         */
22958        "beforemove" : true,
22959        /**
22960         * @event beforeinsert
22961         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22962         * @param {Tree} tree The owner tree
22963         * @param {Node} parent The parent node
22964         * @param {Node} node The child node to be inserted
22965         * @param {Node} refNode The child node the node is being inserted before
22966         */
22967        "beforeinsert" : true
22968    });
22969
22970     Roo.data.Tree.superclass.constructor.call(this);
22971 };
22972
22973 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22974     pathSeparator: "/",
22975
22976     proxyNodeEvent : function(){
22977         return this.fireEvent.apply(this, arguments);
22978     },
22979
22980     /**
22981      * Returns the root node for this tree.
22982      * @return {Node}
22983      */
22984     getRootNode : function(){
22985         return this.root;
22986     },
22987
22988     /**
22989      * Sets the root node for this tree.
22990      * @param {Node} node
22991      * @return {Node}
22992      */
22993     setRootNode : function(node){
22994         this.root = node;
22995         node.ownerTree = this;
22996         node.isRoot = true;
22997         this.registerNode(node);
22998         return node;
22999     },
23000
23001     /**
23002      * Gets a node in this tree by its id.
23003      * @param {String} id
23004      * @return {Node}
23005      */
23006     getNodeById : function(id){
23007         return this.nodeHash[id];
23008     },
23009
23010     registerNode : function(node){
23011         this.nodeHash[node.id] = node;
23012     },
23013
23014     unregisterNode : function(node){
23015         delete this.nodeHash[node.id];
23016     },
23017
23018     toString : function(){
23019         return "[Tree"+(this.id?" "+this.id:"")+"]";
23020     }
23021 });
23022
23023 /**
23024  * @class Roo.data.Node
23025  * @extends Roo.util.Observable
23026  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23027  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23028  * @constructor
23029  * @param {Object} attributes The attributes/config for the node
23030  */
23031 Roo.data.Node = function(attributes){
23032     /**
23033      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23034      * @type {Object}
23035      */
23036     this.attributes = attributes || {};
23037     this.leaf = this.attributes.leaf;
23038     /**
23039      * The node id. @type String
23040      */
23041     this.id = this.attributes.id;
23042     if(!this.id){
23043         this.id = Roo.id(null, "ynode-");
23044         this.attributes.id = this.id;
23045     }
23046      
23047     
23048     /**
23049      * All child nodes of this node. @type Array
23050      */
23051     this.childNodes = [];
23052     if(!this.childNodes.indexOf){ // indexOf is a must
23053         this.childNodes.indexOf = function(o){
23054             for(var i = 0, len = this.length; i < len; i++){
23055                 if(this[i] == o) {
23056                     return i;
23057                 }
23058             }
23059             return -1;
23060         };
23061     }
23062     /**
23063      * The parent node for this node. @type Node
23064      */
23065     this.parentNode = null;
23066     /**
23067      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23068      */
23069     this.firstChild = null;
23070     /**
23071      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23072      */
23073     this.lastChild = null;
23074     /**
23075      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23076      */
23077     this.previousSibling = null;
23078     /**
23079      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23080      */
23081     this.nextSibling = null;
23082
23083     this.addEvents({
23084        /**
23085         * @event append
23086         * Fires when a new child node is appended
23087         * @param {Tree} tree The owner tree
23088         * @param {Node} this This node
23089         * @param {Node} node The newly appended node
23090         * @param {Number} index The index of the newly appended node
23091         */
23092        "append" : true,
23093        /**
23094         * @event remove
23095         * Fires when a child node is removed
23096         * @param {Tree} tree The owner tree
23097         * @param {Node} this This node
23098         * @param {Node} node The removed node
23099         */
23100        "remove" : true,
23101        /**
23102         * @event move
23103         * Fires when this node is moved to a new location in the tree
23104         * @param {Tree} tree The owner tree
23105         * @param {Node} this This node
23106         * @param {Node} oldParent The old parent of this node
23107         * @param {Node} newParent The new parent of this node
23108         * @param {Number} index The index it was moved to
23109         */
23110        "move" : true,
23111        /**
23112         * @event insert
23113         * Fires when a new child node is inserted.
23114         * @param {Tree} tree The owner tree
23115         * @param {Node} this This node
23116         * @param {Node} node The child node inserted
23117         * @param {Node} refNode The child node the node was inserted before
23118         */
23119        "insert" : true,
23120        /**
23121         * @event beforeappend
23122         * Fires before a new child is appended, return false to cancel the append.
23123         * @param {Tree} tree The owner tree
23124         * @param {Node} this This node
23125         * @param {Node} node The child node to be appended
23126         */
23127        "beforeappend" : true,
23128        /**
23129         * @event beforeremove
23130         * Fires before a child is removed, return false to cancel the remove.
23131         * @param {Tree} tree The owner tree
23132         * @param {Node} this This node
23133         * @param {Node} node The child node to be removed
23134         */
23135        "beforeremove" : true,
23136        /**
23137         * @event beforemove
23138         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23139         * @param {Tree} tree The owner tree
23140         * @param {Node} this This node
23141         * @param {Node} oldParent The parent of this node
23142         * @param {Node} newParent The new parent this node is moving to
23143         * @param {Number} index The index it is being moved to
23144         */
23145        "beforemove" : true,
23146        /**
23147         * @event beforeinsert
23148         * Fires before a new child is inserted, return false to cancel the insert.
23149         * @param {Tree} tree The owner tree
23150         * @param {Node} this This node
23151         * @param {Node} node The child node to be inserted
23152         * @param {Node} refNode The child node the node is being inserted before
23153         */
23154        "beforeinsert" : true
23155    });
23156     this.listeners = this.attributes.listeners;
23157     Roo.data.Node.superclass.constructor.call(this);
23158 };
23159
23160 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23161     fireEvent : function(evtName){
23162         // first do standard event for this node
23163         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23164             return false;
23165         }
23166         // then bubble it up to the tree if the event wasn't cancelled
23167         var ot = this.getOwnerTree();
23168         if(ot){
23169             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23170                 return false;
23171             }
23172         }
23173         return true;
23174     },
23175
23176     /**
23177      * Returns true if this node is a leaf
23178      * @return {Boolean}
23179      */
23180     isLeaf : function(){
23181         return this.leaf === true;
23182     },
23183
23184     // private
23185     setFirstChild : function(node){
23186         this.firstChild = node;
23187     },
23188
23189     //private
23190     setLastChild : function(node){
23191         this.lastChild = node;
23192     },
23193
23194
23195     /**
23196      * Returns true if this node is the last child of its parent
23197      * @return {Boolean}
23198      */
23199     isLast : function(){
23200        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23201     },
23202
23203     /**
23204      * Returns true if this node is the first child of its parent
23205      * @return {Boolean}
23206      */
23207     isFirst : function(){
23208        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23209     },
23210
23211     hasChildNodes : function(){
23212         return !this.isLeaf() && this.childNodes.length > 0;
23213     },
23214
23215     /**
23216      * Insert node(s) as the last child node of this node.
23217      * @param {Node/Array} node The node or Array of nodes to append
23218      * @return {Node} The appended node if single append, or null if an array was passed
23219      */
23220     appendChild : function(node){
23221         var multi = false;
23222         if(node instanceof Array){
23223             multi = node;
23224         }else if(arguments.length > 1){
23225             multi = arguments;
23226         }
23227         // if passed an array or multiple args do them one by one
23228         if(multi){
23229             for(var i = 0, len = multi.length; i < len; i++) {
23230                 this.appendChild(multi[i]);
23231             }
23232         }else{
23233             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23234                 return false;
23235             }
23236             var index = this.childNodes.length;
23237             var oldParent = node.parentNode;
23238             // it's a move, make sure we move it cleanly
23239             if(oldParent){
23240                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23241                     return false;
23242                 }
23243                 oldParent.removeChild(node);
23244             }
23245             index = this.childNodes.length;
23246             if(index == 0){
23247                 this.setFirstChild(node);
23248             }
23249             this.childNodes.push(node);
23250             node.parentNode = this;
23251             var ps = this.childNodes[index-1];
23252             if(ps){
23253                 node.previousSibling = ps;
23254                 ps.nextSibling = node;
23255             }else{
23256                 node.previousSibling = null;
23257             }
23258             node.nextSibling = null;
23259             this.setLastChild(node);
23260             node.setOwnerTree(this.getOwnerTree());
23261             this.fireEvent("append", this.ownerTree, this, node, index);
23262             if(oldParent){
23263                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23264             }
23265             return node;
23266         }
23267     },
23268
23269     /**
23270      * Removes a child node from this node.
23271      * @param {Node} node The node to remove
23272      * @return {Node} The removed node
23273      */
23274     removeChild : function(node){
23275         var index = this.childNodes.indexOf(node);
23276         if(index == -1){
23277             return false;
23278         }
23279         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23280             return false;
23281         }
23282
23283         // remove it from childNodes collection
23284         this.childNodes.splice(index, 1);
23285
23286         // update siblings
23287         if(node.previousSibling){
23288             node.previousSibling.nextSibling = node.nextSibling;
23289         }
23290         if(node.nextSibling){
23291             node.nextSibling.previousSibling = node.previousSibling;
23292         }
23293
23294         // update child refs
23295         if(this.firstChild == node){
23296             this.setFirstChild(node.nextSibling);
23297         }
23298         if(this.lastChild == node){
23299             this.setLastChild(node.previousSibling);
23300         }
23301
23302         node.setOwnerTree(null);
23303         // clear any references from the node
23304         node.parentNode = null;
23305         node.previousSibling = null;
23306         node.nextSibling = null;
23307         this.fireEvent("remove", this.ownerTree, this, node);
23308         return node;
23309     },
23310
23311     /**
23312      * Inserts the first node before the second node in this nodes childNodes collection.
23313      * @param {Node} node The node to insert
23314      * @param {Node} refNode The node to insert before (if null the node is appended)
23315      * @return {Node} The inserted node
23316      */
23317     insertBefore : function(node, refNode){
23318         if(!refNode){ // like standard Dom, refNode can be null for append
23319             return this.appendChild(node);
23320         }
23321         // nothing to do
23322         if(node == refNode){
23323             return false;
23324         }
23325
23326         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23327             return false;
23328         }
23329         var index = this.childNodes.indexOf(refNode);
23330         var oldParent = node.parentNode;
23331         var refIndex = index;
23332
23333         // when moving internally, indexes will change after remove
23334         if(oldParent == this && this.childNodes.indexOf(node) < index){
23335             refIndex--;
23336         }
23337
23338         // it's a move, make sure we move it cleanly
23339         if(oldParent){
23340             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23341                 return false;
23342             }
23343             oldParent.removeChild(node);
23344         }
23345         if(refIndex == 0){
23346             this.setFirstChild(node);
23347         }
23348         this.childNodes.splice(refIndex, 0, node);
23349         node.parentNode = this;
23350         var ps = this.childNodes[refIndex-1];
23351         if(ps){
23352             node.previousSibling = ps;
23353             ps.nextSibling = node;
23354         }else{
23355             node.previousSibling = null;
23356         }
23357         node.nextSibling = refNode;
23358         refNode.previousSibling = node;
23359         node.setOwnerTree(this.getOwnerTree());
23360         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23361         if(oldParent){
23362             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23363         }
23364         return node;
23365     },
23366
23367     /**
23368      * Returns the child node at the specified index.
23369      * @param {Number} index
23370      * @return {Node}
23371      */
23372     item : function(index){
23373         return this.childNodes[index];
23374     },
23375
23376     /**
23377      * Replaces one child node in this node with another.
23378      * @param {Node} newChild The replacement node
23379      * @param {Node} oldChild The node to replace
23380      * @return {Node} The replaced node
23381      */
23382     replaceChild : function(newChild, oldChild){
23383         this.insertBefore(newChild, oldChild);
23384         this.removeChild(oldChild);
23385         return oldChild;
23386     },
23387
23388     /**
23389      * Returns the index of a child node
23390      * @param {Node} node
23391      * @return {Number} The index of the node or -1 if it was not found
23392      */
23393     indexOf : function(child){
23394         return this.childNodes.indexOf(child);
23395     },
23396
23397     /**
23398      * Returns the tree this node is in.
23399      * @return {Tree}
23400      */
23401     getOwnerTree : function(){
23402         // if it doesn't have one, look for one
23403         if(!this.ownerTree){
23404             var p = this;
23405             while(p){
23406                 if(p.ownerTree){
23407                     this.ownerTree = p.ownerTree;
23408                     break;
23409                 }
23410                 p = p.parentNode;
23411             }
23412         }
23413         return this.ownerTree;
23414     },
23415
23416     /**
23417      * Returns depth of this node (the root node has a depth of 0)
23418      * @return {Number}
23419      */
23420     getDepth : function(){
23421         var depth = 0;
23422         var p = this;
23423         while(p.parentNode){
23424             ++depth;
23425             p = p.parentNode;
23426         }
23427         return depth;
23428     },
23429
23430     // private
23431     setOwnerTree : function(tree){
23432         // if it's move, we need to update everyone
23433         if(tree != this.ownerTree){
23434             if(this.ownerTree){
23435                 this.ownerTree.unregisterNode(this);
23436             }
23437             this.ownerTree = tree;
23438             var cs = this.childNodes;
23439             for(var i = 0, len = cs.length; i < len; i++) {
23440                 cs[i].setOwnerTree(tree);
23441             }
23442             if(tree){
23443                 tree.registerNode(this);
23444             }
23445         }
23446     },
23447
23448     /**
23449      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23450      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23451      * @return {String} The path
23452      */
23453     getPath : function(attr){
23454         attr = attr || "id";
23455         var p = this.parentNode;
23456         var b = [this.attributes[attr]];
23457         while(p){
23458             b.unshift(p.attributes[attr]);
23459             p = p.parentNode;
23460         }
23461         var sep = this.getOwnerTree().pathSeparator;
23462         return sep + b.join(sep);
23463     },
23464
23465     /**
23466      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23467      * function call will be the scope provided or the current node. The arguments to the function
23468      * will be the args provided or the current node. If the function returns false at any point,
23469      * the bubble is stopped.
23470      * @param {Function} fn The function to call
23471      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23472      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23473      */
23474     bubble : function(fn, scope, args){
23475         var p = this;
23476         while(p){
23477             if(fn.call(scope || p, args || p) === false){
23478                 break;
23479             }
23480             p = p.parentNode;
23481         }
23482     },
23483
23484     /**
23485      * Cascades down the tree from 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 cascade is stopped on that branch.
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     cascade : function(fn, scope, args){
23494         if(fn.call(scope || this, args || this) !== false){
23495             var cs = this.childNodes;
23496             for(var i = 0, len = cs.length; i < len; i++) {
23497                 cs[i].cascade(fn, scope, args);
23498             }
23499         }
23500     },
23501
23502     /**
23503      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23504      * function call will be the scope provided or the current node. The arguments to the function
23505      * will be the args provided or the current node. If the function returns false at any point,
23506      * the iteration stops.
23507      * @param {Function} fn The function to call
23508      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23509      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23510      */
23511     eachChild : function(fn, scope, args){
23512         var cs = this.childNodes;
23513         for(var i = 0, len = cs.length; i < len; i++) {
23514                 if(fn.call(scope || this, args || cs[i]) === false){
23515                     break;
23516                 }
23517         }
23518     },
23519
23520     /**
23521      * Finds the first child that has the attribute with the specified value.
23522      * @param {String} attribute The attribute name
23523      * @param {Mixed} value The value to search for
23524      * @return {Node} The found child or null if none was found
23525      */
23526     findChild : function(attribute, value){
23527         var cs = this.childNodes;
23528         for(var i = 0, len = cs.length; i < len; i++) {
23529                 if(cs[i].attributes[attribute] == value){
23530                     return cs[i];
23531                 }
23532         }
23533         return null;
23534     },
23535
23536     /**
23537      * Finds the first child by a custom function. The child matches if the function passed
23538      * returns true.
23539      * @param {Function} fn
23540      * @param {Object} scope (optional)
23541      * @return {Node} The found child or null if none was found
23542      */
23543     findChildBy : function(fn, scope){
23544         var cs = this.childNodes;
23545         for(var i = 0, len = cs.length; i < len; i++) {
23546                 if(fn.call(scope||cs[i], cs[i]) === true){
23547                     return cs[i];
23548                 }
23549         }
23550         return null;
23551     },
23552
23553     /**
23554      * Sorts this nodes children using the supplied sort function
23555      * @param {Function} fn
23556      * @param {Object} scope (optional)
23557      */
23558     sort : function(fn, scope){
23559         var cs = this.childNodes;
23560         var len = cs.length;
23561         if(len > 0){
23562             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23563             cs.sort(sortFn);
23564             for(var i = 0; i < len; i++){
23565                 var n = cs[i];
23566                 n.previousSibling = cs[i-1];
23567                 n.nextSibling = cs[i+1];
23568                 if(i == 0){
23569                     this.setFirstChild(n);
23570                 }
23571                 if(i == len-1){
23572                     this.setLastChild(n);
23573                 }
23574             }
23575         }
23576     },
23577
23578     /**
23579      * Returns true if this node is an ancestor (at any point) of the passed node.
23580      * @param {Node} node
23581      * @return {Boolean}
23582      */
23583     contains : function(node){
23584         return node.isAncestor(this);
23585     },
23586
23587     /**
23588      * Returns true if the passed node is an ancestor (at any point) of this node.
23589      * @param {Node} node
23590      * @return {Boolean}
23591      */
23592     isAncestor : function(node){
23593         var p = this.parentNode;
23594         while(p){
23595             if(p == node){
23596                 return true;
23597             }
23598             p = p.parentNode;
23599         }
23600         return false;
23601     },
23602
23603     toString : function(){
23604         return "[Node"+(this.id?" "+this.id:"")+"]";
23605     }
23606 });/*
23607  * Based on:
23608  * Ext JS Library 1.1.1
23609  * Copyright(c) 2006-2007, Ext JS, LLC.
23610  *
23611  * Originally Released Under LGPL - original licence link has changed is not relivant.
23612  *
23613  * Fork - LGPL
23614  * <script type="text/javascript">
23615  */
23616  (function(){ 
23617 /**
23618  * @class Roo.Layer
23619  * @extends Roo.Element
23620  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23621  * automatic maintaining of shadow/shim positions.
23622  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23623  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23624  * you can pass a string with a CSS class name. False turns off the shadow.
23625  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23626  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23627  * @cfg {String} cls CSS class to add to the element
23628  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23629  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23630  * @constructor
23631  * @param {Object} config An object with config options.
23632  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23633  */
23634
23635 Roo.Layer = function(config, existingEl){
23636     config = config || {};
23637     var dh = Roo.DomHelper;
23638     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23639     if(existingEl){
23640         this.dom = Roo.getDom(existingEl);
23641     }
23642     if(!this.dom){
23643         var o = config.dh || {tag: "div", cls: "x-layer"};
23644         this.dom = dh.append(pel, o);
23645     }
23646     if(config.cls){
23647         this.addClass(config.cls);
23648     }
23649     this.constrain = config.constrain !== false;
23650     this.visibilityMode = Roo.Element.VISIBILITY;
23651     if(config.id){
23652         this.id = this.dom.id = config.id;
23653     }else{
23654         this.id = Roo.id(this.dom);
23655     }
23656     this.zindex = config.zindex || this.getZIndex();
23657     this.position("absolute", this.zindex);
23658     if(config.shadow){
23659         this.shadowOffset = config.shadowOffset || 4;
23660         this.shadow = new Roo.Shadow({
23661             offset : this.shadowOffset,
23662             mode : config.shadow
23663         });
23664     }else{
23665         this.shadowOffset = 0;
23666     }
23667     this.useShim = config.shim !== false && Roo.useShims;
23668     this.useDisplay = config.useDisplay;
23669     this.hide();
23670 };
23671
23672 var supr = Roo.Element.prototype;
23673
23674 // shims are shared among layer to keep from having 100 iframes
23675 var shims = [];
23676
23677 Roo.extend(Roo.Layer, Roo.Element, {
23678
23679     getZIndex : function(){
23680         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23681     },
23682
23683     getShim : function(){
23684         if(!this.useShim){
23685             return null;
23686         }
23687         if(this.shim){
23688             return this.shim;
23689         }
23690         var shim = shims.shift();
23691         if(!shim){
23692             shim = this.createShim();
23693             shim.enableDisplayMode('block');
23694             shim.dom.style.display = 'none';
23695             shim.dom.style.visibility = 'visible';
23696         }
23697         var pn = this.dom.parentNode;
23698         if(shim.dom.parentNode != pn){
23699             pn.insertBefore(shim.dom, this.dom);
23700         }
23701         shim.setStyle('z-index', this.getZIndex()-2);
23702         this.shim = shim;
23703         return shim;
23704     },
23705
23706     hideShim : function(){
23707         if(this.shim){
23708             this.shim.setDisplayed(false);
23709             shims.push(this.shim);
23710             delete this.shim;
23711         }
23712     },
23713
23714     disableShadow : function(){
23715         if(this.shadow){
23716             this.shadowDisabled = true;
23717             this.shadow.hide();
23718             this.lastShadowOffset = this.shadowOffset;
23719             this.shadowOffset = 0;
23720         }
23721     },
23722
23723     enableShadow : function(show){
23724         if(this.shadow){
23725             this.shadowDisabled = false;
23726             this.shadowOffset = this.lastShadowOffset;
23727             delete this.lastShadowOffset;
23728             if(show){
23729                 this.sync(true);
23730             }
23731         }
23732     },
23733
23734     // private
23735     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23736     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23737     sync : function(doShow){
23738         var sw = this.shadow;
23739         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23740             var sh = this.getShim();
23741
23742             var w = this.getWidth(),
23743                 h = this.getHeight();
23744
23745             var l = this.getLeft(true),
23746                 t = this.getTop(true);
23747
23748             if(sw && !this.shadowDisabled){
23749                 if(doShow && !sw.isVisible()){
23750                     sw.show(this);
23751                 }else{
23752                     sw.realign(l, t, w, h);
23753                 }
23754                 if(sh){
23755                     if(doShow){
23756                        sh.show();
23757                     }
23758                     // fit the shim behind the shadow, so it is shimmed too
23759                     var a = sw.adjusts, s = sh.dom.style;
23760                     s.left = (Math.min(l, l+a.l))+"px";
23761                     s.top = (Math.min(t, t+a.t))+"px";
23762                     s.width = (w+a.w)+"px";
23763                     s.height = (h+a.h)+"px";
23764                 }
23765             }else if(sh){
23766                 if(doShow){
23767                    sh.show();
23768                 }
23769                 sh.setSize(w, h);
23770                 sh.setLeftTop(l, t);
23771             }
23772             
23773         }
23774     },
23775
23776     // private
23777     destroy : function(){
23778         this.hideShim();
23779         if(this.shadow){
23780             this.shadow.hide();
23781         }
23782         this.removeAllListeners();
23783         var pn = this.dom.parentNode;
23784         if(pn){
23785             pn.removeChild(this.dom);
23786         }
23787         Roo.Element.uncache(this.id);
23788     },
23789
23790     remove : function(){
23791         this.destroy();
23792     },
23793
23794     // private
23795     beginUpdate : function(){
23796         this.updating = true;
23797     },
23798
23799     // private
23800     endUpdate : function(){
23801         this.updating = false;
23802         this.sync(true);
23803     },
23804
23805     // private
23806     hideUnders : function(negOffset){
23807         if(this.shadow){
23808             this.shadow.hide();
23809         }
23810         this.hideShim();
23811     },
23812
23813     // private
23814     constrainXY : function(){
23815         if(this.constrain){
23816             var vw = Roo.lib.Dom.getViewWidth(),
23817                 vh = Roo.lib.Dom.getViewHeight();
23818             var s = Roo.get(document).getScroll();
23819
23820             var xy = this.getXY();
23821             var x = xy[0], y = xy[1];   
23822             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23823             // only move it if it needs it
23824             var moved = false;
23825             // first validate right/bottom
23826             if((x + w) > vw+s.left){
23827                 x = vw - w - this.shadowOffset;
23828                 moved = true;
23829             }
23830             if((y + h) > vh+s.top){
23831                 y = vh - h - this.shadowOffset;
23832                 moved = true;
23833             }
23834             // then make sure top/left isn't negative
23835             if(x < s.left){
23836                 x = s.left;
23837                 moved = true;
23838             }
23839             if(y < s.top){
23840                 y = s.top;
23841                 moved = true;
23842             }
23843             if(moved){
23844                 if(this.avoidY){
23845                     var ay = this.avoidY;
23846                     if(y <= ay && (y+h) >= ay){
23847                         y = ay-h-5;   
23848                     }
23849                 }
23850                 xy = [x, y];
23851                 this.storeXY(xy);
23852                 supr.setXY.call(this, xy);
23853                 this.sync();
23854             }
23855         }
23856     },
23857
23858     isVisible : function(){
23859         return this.visible;    
23860     },
23861
23862     // private
23863     showAction : function(){
23864         this.visible = true; // track visibility to prevent getStyle calls
23865         if(this.useDisplay === true){
23866             this.setDisplayed("");
23867         }else if(this.lastXY){
23868             supr.setXY.call(this, this.lastXY);
23869         }else if(this.lastLT){
23870             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23871         }
23872     },
23873
23874     // private
23875     hideAction : function(){
23876         this.visible = false;
23877         if(this.useDisplay === true){
23878             this.setDisplayed(false);
23879         }else{
23880             this.setLeftTop(-10000,-10000);
23881         }
23882     },
23883
23884     // overridden Element method
23885     setVisible : function(v, a, d, c, e){
23886         if(v){
23887             this.showAction();
23888         }
23889         if(a && v){
23890             var cb = function(){
23891                 this.sync(true);
23892                 if(c){
23893                     c();
23894                 }
23895             }.createDelegate(this);
23896             supr.setVisible.call(this, true, true, d, cb, e);
23897         }else{
23898             if(!v){
23899                 this.hideUnders(true);
23900             }
23901             var cb = c;
23902             if(a){
23903                 cb = function(){
23904                     this.hideAction();
23905                     if(c){
23906                         c();
23907                     }
23908                 }.createDelegate(this);
23909             }
23910             supr.setVisible.call(this, v, a, d, cb, e);
23911             if(v){
23912                 this.sync(true);
23913             }else if(!a){
23914                 this.hideAction();
23915             }
23916         }
23917     },
23918
23919     storeXY : function(xy){
23920         delete this.lastLT;
23921         this.lastXY = xy;
23922     },
23923
23924     storeLeftTop : function(left, top){
23925         delete this.lastXY;
23926         this.lastLT = [left, top];
23927     },
23928
23929     // private
23930     beforeFx : function(){
23931         this.beforeAction();
23932         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23933     },
23934
23935     // private
23936     afterFx : function(){
23937         Roo.Layer.superclass.afterFx.apply(this, arguments);
23938         this.sync(this.isVisible());
23939     },
23940
23941     // private
23942     beforeAction : function(){
23943         if(!this.updating && this.shadow){
23944             this.shadow.hide();
23945         }
23946     },
23947
23948     // overridden Element method
23949     setLeft : function(left){
23950         this.storeLeftTop(left, this.getTop(true));
23951         supr.setLeft.apply(this, arguments);
23952         this.sync();
23953     },
23954
23955     setTop : function(top){
23956         this.storeLeftTop(this.getLeft(true), top);
23957         supr.setTop.apply(this, arguments);
23958         this.sync();
23959     },
23960
23961     setLeftTop : function(left, top){
23962         this.storeLeftTop(left, top);
23963         supr.setLeftTop.apply(this, arguments);
23964         this.sync();
23965     },
23966
23967     setXY : function(xy, a, d, c, e){
23968         this.fixDisplay();
23969         this.beforeAction();
23970         this.storeXY(xy);
23971         var cb = this.createCB(c);
23972         supr.setXY.call(this, xy, a, d, cb, e);
23973         if(!a){
23974             cb();
23975         }
23976     },
23977
23978     // private
23979     createCB : function(c){
23980         var el = this;
23981         return function(){
23982             el.constrainXY();
23983             el.sync(true);
23984             if(c){
23985                 c();
23986             }
23987         };
23988     },
23989
23990     // overridden Element method
23991     setX : function(x, a, d, c, e){
23992         this.setXY([x, this.getY()], a, d, c, e);
23993     },
23994
23995     // overridden Element method
23996     setY : function(y, a, d, c, e){
23997         this.setXY([this.getX(), y], a, d, c, e);
23998     },
23999
24000     // overridden Element method
24001     setSize : function(w, h, a, d, c, e){
24002         this.beforeAction();
24003         var cb = this.createCB(c);
24004         supr.setSize.call(this, w, h, a, d, cb, e);
24005         if(!a){
24006             cb();
24007         }
24008     },
24009
24010     // overridden Element method
24011     setWidth : function(w, a, d, c, e){
24012         this.beforeAction();
24013         var cb = this.createCB(c);
24014         supr.setWidth.call(this, w, a, d, cb, e);
24015         if(!a){
24016             cb();
24017         }
24018     },
24019
24020     // overridden Element method
24021     setHeight : function(h, a, d, c, e){
24022         this.beforeAction();
24023         var cb = this.createCB(c);
24024         supr.setHeight.call(this, h, a, d, cb, e);
24025         if(!a){
24026             cb();
24027         }
24028     },
24029
24030     // overridden Element method
24031     setBounds : function(x, y, w, h, a, d, c, e){
24032         this.beforeAction();
24033         var cb = this.createCB(c);
24034         if(!a){
24035             this.storeXY([x, y]);
24036             supr.setXY.call(this, [x, y]);
24037             supr.setSize.call(this, w, h, a, d, cb, e);
24038             cb();
24039         }else{
24040             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24041         }
24042         return this;
24043     },
24044     
24045     /**
24046      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24047      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24048      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24049      * @param {Number} zindex The new z-index to set
24050      * @return {this} The Layer
24051      */
24052     setZIndex : function(zindex){
24053         this.zindex = zindex;
24054         this.setStyle("z-index", zindex + 2);
24055         if(this.shadow){
24056             this.shadow.setZIndex(zindex + 1);
24057         }
24058         if(this.shim){
24059             this.shim.setStyle("z-index", zindex);
24060         }
24061     }
24062 });
24063 })();/*
24064  * Based on:
24065  * Ext JS Library 1.1.1
24066  * Copyright(c) 2006-2007, Ext JS, LLC.
24067  *
24068  * Originally Released Under LGPL - original licence link has changed is not relivant.
24069  *
24070  * Fork - LGPL
24071  * <script type="text/javascript">
24072  */
24073
24074
24075 /**
24076  * @class Roo.Shadow
24077  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24078  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24079  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24080  * @constructor
24081  * Create a new Shadow
24082  * @param {Object} config The config object
24083  */
24084 Roo.Shadow = function(config){
24085     Roo.apply(this, config);
24086     if(typeof this.mode != "string"){
24087         this.mode = this.defaultMode;
24088     }
24089     var o = this.offset, a = {h: 0};
24090     var rad = Math.floor(this.offset/2);
24091     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24092         case "drop":
24093             a.w = 0;
24094             a.l = a.t = o;
24095             a.t -= 1;
24096             if(Roo.isIE){
24097                 a.l -= this.offset + rad;
24098                 a.t -= this.offset + rad;
24099                 a.w -= rad;
24100                 a.h -= rad;
24101                 a.t += 1;
24102             }
24103         break;
24104         case "sides":
24105             a.w = (o*2);
24106             a.l = -o;
24107             a.t = o-1;
24108             if(Roo.isIE){
24109                 a.l -= (this.offset - rad);
24110                 a.t -= this.offset + rad;
24111                 a.l += 1;
24112                 a.w -= (this.offset - rad)*2;
24113                 a.w -= rad + 1;
24114                 a.h -= 1;
24115             }
24116         break;
24117         case "frame":
24118             a.w = a.h = (o*2);
24119             a.l = a.t = -o;
24120             a.t += 1;
24121             a.h -= 2;
24122             if(Roo.isIE){
24123                 a.l -= (this.offset - rad);
24124                 a.t -= (this.offset - rad);
24125                 a.l += 1;
24126                 a.w -= (this.offset + rad + 1);
24127                 a.h -= (this.offset + rad);
24128                 a.h += 1;
24129             }
24130         break;
24131     };
24132
24133     this.adjusts = a;
24134 };
24135
24136 Roo.Shadow.prototype = {
24137     /**
24138      * @cfg {String} mode
24139      * The shadow display mode.  Supports the following options:<br />
24140      * sides: Shadow displays on both sides and bottom only<br />
24141      * frame: Shadow displays equally on all four sides<br />
24142      * drop: Traditional bottom-right drop shadow (default)
24143      */
24144     /**
24145      * @cfg {String} offset
24146      * The number of pixels to offset the shadow from the element (defaults to 4)
24147      */
24148     offset: 4,
24149
24150     // private
24151     defaultMode: "drop",
24152
24153     /**
24154      * Displays the shadow under the target element
24155      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24156      */
24157     show : function(target){
24158         target = Roo.get(target);
24159         if(!this.el){
24160             this.el = Roo.Shadow.Pool.pull();
24161             if(this.el.dom.nextSibling != target.dom){
24162                 this.el.insertBefore(target);
24163             }
24164         }
24165         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24166         if(Roo.isIE){
24167             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24168         }
24169         this.realign(
24170             target.getLeft(true),
24171             target.getTop(true),
24172             target.getWidth(),
24173             target.getHeight()
24174         );
24175         this.el.dom.style.display = "block";
24176     },
24177
24178     /**
24179      * Returns true if the shadow is visible, else false
24180      */
24181     isVisible : function(){
24182         return this.el ? true : false;  
24183     },
24184
24185     /**
24186      * Direct alignment when values are already available. Show must be called at least once before
24187      * calling this method to ensure it is initialized.
24188      * @param {Number} left The target element left position
24189      * @param {Number} top The target element top position
24190      * @param {Number} width The target element width
24191      * @param {Number} height The target element height
24192      */
24193     realign : function(l, t, w, h){
24194         if(!this.el){
24195             return;
24196         }
24197         var a = this.adjusts, d = this.el.dom, s = d.style;
24198         var iea = 0;
24199         s.left = (l+a.l)+"px";
24200         s.top = (t+a.t)+"px";
24201         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24202  
24203         if(s.width != sws || s.height != shs){
24204             s.width = sws;
24205             s.height = shs;
24206             if(!Roo.isIE){
24207                 var cn = d.childNodes;
24208                 var sww = Math.max(0, (sw-12))+"px";
24209                 cn[0].childNodes[1].style.width = sww;
24210                 cn[1].childNodes[1].style.width = sww;
24211                 cn[2].childNodes[1].style.width = sww;
24212                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24213             }
24214         }
24215     },
24216
24217     /**
24218      * Hides this shadow
24219      */
24220     hide : function(){
24221         if(this.el){
24222             this.el.dom.style.display = "none";
24223             Roo.Shadow.Pool.push(this.el);
24224             delete this.el;
24225         }
24226     },
24227
24228     /**
24229      * Adjust the z-index of this shadow
24230      * @param {Number} zindex The new z-index
24231      */
24232     setZIndex : function(z){
24233         this.zIndex = z;
24234         if(this.el){
24235             this.el.setStyle("z-index", z);
24236         }
24237     }
24238 };
24239
24240 // Private utility class that manages the internal Shadow cache
24241 Roo.Shadow.Pool = function(){
24242     var p = [];
24243     var markup = Roo.isIE ?
24244                  '<div class="x-ie-shadow"></div>' :
24245                  '<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>';
24246     return {
24247         pull : function(){
24248             var sh = p.shift();
24249             if(!sh){
24250                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24251                 sh.autoBoxAdjust = false;
24252             }
24253             return sh;
24254         },
24255
24256         push : function(sh){
24257             p.push(sh);
24258         }
24259     };
24260 }();/*
24261  * Based on:
24262  * Ext JS Library 1.1.1
24263  * Copyright(c) 2006-2007, Ext JS, LLC.
24264  *
24265  * Originally Released Under LGPL - original licence link has changed is not relivant.
24266  *
24267  * Fork - LGPL
24268  * <script type="text/javascript">
24269  */
24270
24271
24272 /**
24273  * @class Roo.SplitBar
24274  * @extends Roo.util.Observable
24275  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24276  * <br><br>
24277  * Usage:
24278  * <pre><code>
24279 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24280                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24281 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24282 split.minSize = 100;
24283 split.maxSize = 600;
24284 split.animate = true;
24285 split.on('moved', splitterMoved);
24286 </code></pre>
24287  * @constructor
24288  * Create a new SplitBar
24289  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24290  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24291  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24292  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24293                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24294                         position of the SplitBar).
24295  */
24296 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24297     
24298     /** @private */
24299     this.el = Roo.get(dragElement, true);
24300     this.el.dom.unselectable = "on";
24301     /** @private */
24302     this.resizingEl = Roo.get(resizingElement, true);
24303
24304     /**
24305      * @private
24306      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24307      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24308      * @type Number
24309      */
24310     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24311     
24312     /**
24313      * The minimum size of the resizing element. (Defaults to 0)
24314      * @type Number
24315      */
24316     this.minSize = 0;
24317     
24318     /**
24319      * The maximum size of the resizing element. (Defaults to 2000)
24320      * @type Number
24321      */
24322     this.maxSize = 2000;
24323     
24324     /**
24325      * Whether to animate the transition to the new size
24326      * @type Boolean
24327      */
24328     this.animate = false;
24329     
24330     /**
24331      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24332      * @type Boolean
24333      */
24334     this.useShim = false;
24335     
24336     /** @private */
24337     this.shim = null;
24338     
24339     if(!existingProxy){
24340         /** @private */
24341         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24342     }else{
24343         this.proxy = Roo.get(existingProxy).dom;
24344     }
24345     /** @private */
24346     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24347     
24348     /** @private */
24349     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24350     
24351     /** @private */
24352     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24353     
24354     /** @private */
24355     this.dragSpecs = {};
24356     
24357     /**
24358      * @private The adapter to use to positon and resize elements
24359      */
24360     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24361     this.adapter.init(this);
24362     
24363     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24364         /** @private */
24365         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24366         this.el.addClass("x-splitbar-h");
24367     }else{
24368         /** @private */
24369         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24370         this.el.addClass("x-splitbar-v");
24371     }
24372     
24373     this.addEvents({
24374         /**
24375          * @event resize
24376          * Fires when the splitter is moved (alias for {@link #event-moved})
24377          * @param {Roo.SplitBar} this
24378          * @param {Number} newSize the new width or height
24379          */
24380         "resize" : true,
24381         /**
24382          * @event moved
24383          * Fires when the splitter is moved
24384          * @param {Roo.SplitBar} this
24385          * @param {Number} newSize the new width or height
24386          */
24387         "moved" : true,
24388         /**
24389          * @event beforeresize
24390          * Fires before the splitter is dragged
24391          * @param {Roo.SplitBar} this
24392          */
24393         "beforeresize" : true,
24394
24395         "beforeapply" : true
24396     });
24397
24398     Roo.util.Observable.call(this);
24399 };
24400
24401 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24402     onStartProxyDrag : function(x, y){
24403         this.fireEvent("beforeresize", this);
24404         if(!this.overlay){
24405             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24406             o.unselectable();
24407             o.enableDisplayMode("block");
24408             // all splitbars share the same overlay
24409             Roo.SplitBar.prototype.overlay = o;
24410         }
24411         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24412         this.overlay.show();
24413         Roo.get(this.proxy).setDisplayed("block");
24414         var size = this.adapter.getElementSize(this);
24415         this.activeMinSize = this.getMinimumSize();;
24416         this.activeMaxSize = this.getMaximumSize();;
24417         var c1 = size - this.activeMinSize;
24418         var c2 = Math.max(this.activeMaxSize - size, 0);
24419         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24420             this.dd.resetConstraints();
24421             this.dd.setXConstraint(
24422                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24423                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24424             );
24425             this.dd.setYConstraint(0, 0);
24426         }else{
24427             this.dd.resetConstraints();
24428             this.dd.setXConstraint(0, 0);
24429             this.dd.setYConstraint(
24430                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24431                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24432             );
24433          }
24434         this.dragSpecs.startSize = size;
24435         this.dragSpecs.startPoint = [x, y];
24436         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24437     },
24438     
24439     /** 
24440      * @private Called after the drag operation by the DDProxy
24441      */
24442     onEndProxyDrag : function(e){
24443         Roo.get(this.proxy).setDisplayed(false);
24444         var endPoint = Roo.lib.Event.getXY(e);
24445         if(this.overlay){
24446             this.overlay.hide();
24447         }
24448         var newSize;
24449         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24450             newSize = this.dragSpecs.startSize + 
24451                 (this.placement == Roo.SplitBar.LEFT ?
24452                     endPoint[0] - this.dragSpecs.startPoint[0] :
24453                     this.dragSpecs.startPoint[0] - endPoint[0]
24454                 );
24455         }else{
24456             newSize = this.dragSpecs.startSize + 
24457                 (this.placement == Roo.SplitBar.TOP ?
24458                     endPoint[1] - this.dragSpecs.startPoint[1] :
24459                     this.dragSpecs.startPoint[1] - endPoint[1]
24460                 );
24461         }
24462         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24463         if(newSize != this.dragSpecs.startSize){
24464             if(this.fireEvent('beforeapply', this, newSize) !== false){
24465                 this.adapter.setElementSize(this, newSize);
24466                 this.fireEvent("moved", this, newSize);
24467                 this.fireEvent("resize", this, newSize);
24468             }
24469         }
24470     },
24471     
24472     /**
24473      * Get the adapter this SplitBar uses
24474      * @return The adapter object
24475      */
24476     getAdapter : function(){
24477         return this.adapter;
24478     },
24479     
24480     /**
24481      * Set the adapter this SplitBar uses
24482      * @param {Object} adapter A SplitBar adapter object
24483      */
24484     setAdapter : function(adapter){
24485         this.adapter = adapter;
24486         this.adapter.init(this);
24487     },
24488     
24489     /**
24490      * Gets the minimum size for the resizing element
24491      * @return {Number} The minimum size
24492      */
24493     getMinimumSize : function(){
24494         return this.minSize;
24495     },
24496     
24497     /**
24498      * Sets the minimum size for the resizing element
24499      * @param {Number} minSize The minimum size
24500      */
24501     setMinimumSize : function(minSize){
24502         this.minSize = minSize;
24503     },
24504     
24505     /**
24506      * Gets the maximum size for the resizing element
24507      * @return {Number} The maximum size
24508      */
24509     getMaximumSize : function(){
24510         return this.maxSize;
24511     },
24512     
24513     /**
24514      * Sets the maximum size for the resizing element
24515      * @param {Number} maxSize The maximum size
24516      */
24517     setMaximumSize : function(maxSize){
24518         this.maxSize = maxSize;
24519     },
24520     
24521     /**
24522      * Sets the initialize size for the resizing element
24523      * @param {Number} size The initial size
24524      */
24525     setCurrentSize : function(size){
24526         var oldAnimate = this.animate;
24527         this.animate = false;
24528         this.adapter.setElementSize(this, size);
24529         this.animate = oldAnimate;
24530     },
24531     
24532     /**
24533      * Destroy this splitbar. 
24534      * @param {Boolean} removeEl True to remove the element
24535      */
24536     destroy : function(removeEl){
24537         if(this.shim){
24538             this.shim.remove();
24539         }
24540         this.dd.unreg();
24541         this.proxy.parentNode.removeChild(this.proxy);
24542         if(removeEl){
24543             this.el.remove();
24544         }
24545     }
24546 });
24547
24548 /**
24549  * @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.
24550  */
24551 Roo.SplitBar.createProxy = function(dir){
24552     var proxy = new Roo.Element(document.createElement("div"));
24553     proxy.unselectable();
24554     var cls = 'x-splitbar-proxy';
24555     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24556     document.body.appendChild(proxy.dom);
24557     return proxy.dom;
24558 };
24559
24560 /** 
24561  * @class Roo.SplitBar.BasicLayoutAdapter
24562  * Default Adapter. It assumes the splitter and resizing element are not positioned
24563  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24564  */
24565 Roo.SplitBar.BasicLayoutAdapter = function(){
24566 };
24567
24568 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24569     // do nothing for now
24570     init : function(s){
24571     
24572     },
24573     /**
24574      * Called before drag operations to get the current size of the resizing element. 
24575      * @param {Roo.SplitBar} s The SplitBar using this adapter
24576      */
24577      getElementSize : function(s){
24578         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24579             return s.resizingEl.getWidth();
24580         }else{
24581             return s.resizingEl.getHeight();
24582         }
24583     },
24584     
24585     /**
24586      * Called after drag operations to set the size of the resizing element.
24587      * @param {Roo.SplitBar} s The SplitBar using this adapter
24588      * @param {Number} newSize The new size to set
24589      * @param {Function} onComplete A function to be invoked when resizing is complete
24590      */
24591     setElementSize : function(s, newSize, onComplete){
24592         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24593             if(!s.animate){
24594                 s.resizingEl.setWidth(newSize);
24595                 if(onComplete){
24596                     onComplete(s, newSize);
24597                 }
24598             }else{
24599                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24600             }
24601         }else{
24602             
24603             if(!s.animate){
24604                 s.resizingEl.setHeight(newSize);
24605                 if(onComplete){
24606                     onComplete(s, newSize);
24607                 }
24608             }else{
24609                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24610             }
24611         }
24612     }
24613 };
24614
24615 /** 
24616  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24617  * @extends Roo.SplitBar.BasicLayoutAdapter
24618  * Adapter that  moves the splitter element to align with the resized sizing element. 
24619  * Used with an absolute positioned SplitBar.
24620  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24621  * document.body, make sure you assign an id to the body element.
24622  */
24623 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24624     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24625     this.container = Roo.get(container);
24626 };
24627
24628 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24629     init : function(s){
24630         this.basic.init(s);
24631     },
24632     
24633     getElementSize : function(s){
24634         return this.basic.getElementSize(s);
24635     },
24636     
24637     setElementSize : function(s, newSize, onComplete){
24638         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24639     },
24640     
24641     moveSplitter : function(s){
24642         var yes = Roo.SplitBar;
24643         switch(s.placement){
24644             case yes.LEFT:
24645                 s.el.setX(s.resizingEl.getRight());
24646                 break;
24647             case yes.RIGHT:
24648                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24649                 break;
24650             case yes.TOP:
24651                 s.el.setY(s.resizingEl.getBottom());
24652                 break;
24653             case yes.BOTTOM:
24654                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24655                 break;
24656         }
24657     }
24658 };
24659
24660 /**
24661  * Orientation constant - Create a vertical SplitBar
24662  * @static
24663  * @type Number
24664  */
24665 Roo.SplitBar.VERTICAL = 1;
24666
24667 /**
24668  * Orientation constant - Create a horizontal SplitBar
24669  * @static
24670  * @type Number
24671  */
24672 Roo.SplitBar.HORIZONTAL = 2;
24673
24674 /**
24675  * Placement constant - The resizing element is to the left of the splitter element
24676  * @static
24677  * @type Number
24678  */
24679 Roo.SplitBar.LEFT = 1;
24680
24681 /**
24682  * Placement constant - The resizing element is to the right of the splitter element
24683  * @static
24684  * @type Number
24685  */
24686 Roo.SplitBar.RIGHT = 2;
24687
24688 /**
24689  * Placement constant - The resizing element is positioned above the splitter element
24690  * @static
24691  * @type Number
24692  */
24693 Roo.SplitBar.TOP = 3;
24694
24695 /**
24696  * Placement constant - The resizing element is positioned under splitter element
24697  * @static
24698  * @type Number
24699  */
24700 Roo.SplitBar.BOTTOM = 4;
24701 /*
24702  * Based on:
24703  * Ext JS Library 1.1.1
24704  * Copyright(c) 2006-2007, Ext JS, LLC.
24705  *
24706  * Originally Released Under LGPL - original licence link has changed is not relivant.
24707  *
24708  * Fork - LGPL
24709  * <script type="text/javascript">
24710  */
24711
24712 /**
24713  * @class Roo.View
24714  * @extends Roo.util.Observable
24715  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24716  * This class also supports single and multi selection modes. <br>
24717  * Create a data model bound view:
24718  <pre><code>
24719  var store = new Roo.data.Store(...);
24720
24721  var view = new Roo.View({
24722     el : "my-element",
24723     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24724  
24725     singleSelect: true,
24726     selectedClass: "ydataview-selected",
24727     store: store
24728  });
24729
24730  // listen for node click?
24731  view.on("click", function(vw, index, node, e){
24732  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24733  });
24734
24735  // load XML data
24736  dataModel.load("foobar.xml");
24737  </code></pre>
24738  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24739  * <br><br>
24740  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24741  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24742  * 
24743  * Note: old style constructor is still suported (container, template, config)
24744  * 
24745  * @constructor
24746  * Create a new View
24747  * @param {Object} config The config object
24748  * 
24749  */
24750 Roo.View = function(config, depreciated_tpl, depreciated_config){
24751     
24752     if (typeof(depreciated_tpl) == 'undefined') {
24753         // new way.. - universal constructor.
24754         Roo.apply(this, config);
24755         this.el  = Roo.get(this.el);
24756     } else {
24757         // old format..
24758         this.el  = Roo.get(config);
24759         this.tpl = depreciated_tpl;
24760         Roo.apply(this, depreciated_config);
24761     }
24762     this.wrapEl  = this.el.wrap().wrap();
24763     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24764     
24765     
24766     if(typeof(this.tpl) == "string"){
24767         this.tpl = new Roo.Template(this.tpl);
24768     } else {
24769         // support xtype ctors..
24770         this.tpl = new Roo.factory(this.tpl, Roo);
24771     }
24772     
24773     
24774     this.tpl.compile();
24775    
24776   
24777     
24778      
24779     /** @private */
24780     this.addEvents({
24781         /**
24782          * @event beforeclick
24783          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
24790         /**
24791          * @event click
24792          * Fires when a template node is 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             "click" : true,
24799         /**
24800          * @event dblclick
24801          * Fires when a template node is double clicked.
24802          * @param {Roo.View} this
24803          * @param {Number} index The index of the target node
24804          * @param {HTMLElement} node The target node
24805          * @param {Roo.EventObject} e The raw event object
24806          */
24807             "dblclick" : true,
24808         /**
24809          * @event contextmenu
24810          * Fires when a template node is right clicked.
24811          * @param {Roo.View} this
24812          * @param {Number} index The index of the target node
24813          * @param {HTMLElement} node The target node
24814          * @param {Roo.EventObject} e The raw event object
24815          */
24816             "contextmenu" : true,
24817         /**
24818          * @event selectionchange
24819          * Fires when the selected nodes change.
24820          * @param {Roo.View} this
24821          * @param {Array} selections Array of the selected nodes
24822          */
24823             "selectionchange" : true,
24824     
24825         /**
24826          * @event beforeselect
24827          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24828          * @param {Roo.View} this
24829          * @param {HTMLElement} node The node to be selected
24830          * @param {Array} selections Array of currently selected nodes
24831          */
24832             "beforeselect" : true,
24833         /**
24834          * @event preparedata
24835          * Fires on every row to render, to allow you to change the data.
24836          * @param {Roo.View} this
24837          * @param {Object} data to be rendered (change this)
24838          */
24839           "preparedata" : true
24840           
24841           
24842         });
24843
24844
24845
24846     this.el.on({
24847         "click": this.onClick,
24848         "dblclick": this.onDblClick,
24849         "contextmenu": this.onContextMenu,
24850         scope:this
24851     });
24852
24853     this.selections = [];
24854     this.nodes = [];
24855     this.cmp = new Roo.CompositeElementLite([]);
24856     if(this.store){
24857         this.store = Roo.factory(this.store, Roo.data);
24858         this.setStore(this.store, true);
24859     }
24860     
24861     if ( this.footer && this.footer.xtype) {
24862            
24863          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24864         
24865         this.footer.dataSource = this.store
24866         this.footer.container = fctr;
24867         this.footer = Roo.factory(this.footer, Roo);
24868         fctr.insertFirst(this.el);
24869         
24870         // this is a bit insane - as the paging toolbar seems to detach the el..
24871 //        dom.parentNode.parentNode.parentNode
24872          // they get detached?
24873     }
24874     
24875     
24876     Roo.View.superclass.constructor.call(this);
24877     
24878     
24879 };
24880
24881 Roo.extend(Roo.View, Roo.util.Observable, {
24882     
24883      /**
24884      * @cfg {Roo.data.Store} store Data store to load data from.
24885      */
24886     store : false,
24887     
24888     /**
24889      * @cfg {String|Roo.Element} el The container element.
24890      */
24891     el : '',
24892     
24893     /**
24894      * @cfg {String|Roo.Template} tpl The template used by this View 
24895      */
24896     tpl : false,
24897     /**
24898      * @cfg {String} dataName the named area of the template to use as the data area
24899      *                          Works with domtemplates roo-name="name"
24900      */
24901     dataName: false,
24902     /**
24903      * @cfg {String} selectedClass The css class to add to selected nodes
24904      */
24905     selectedClass : "x-view-selected",
24906      /**
24907      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24908      */
24909     emptyText : "",
24910     
24911     /**
24912      * @cfg {String} text to display on mask (default Loading)
24913      */
24914     mask : false,
24915     /**
24916      * @cfg {Boolean} multiSelect Allow multiple selection
24917      */
24918     multiSelect : false,
24919     /**
24920      * @cfg {Boolean} singleSelect Allow single selection
24921      */
24922     singleSelect:  false,
24923     
24924     /**
24925      * @cfg {Boolean} toggleSelect - selecting 
24926      */
24927     toggleSelect : false,
24928     
24929     /**
24930      * Returns the element this view is bound to.
24931      * @return {Roo.Element}
24932      */
24933     getEl : function(){
24934         return this.wrapEl;
24935     },
24936     
24937     
24938
24939     /**
24940      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24941      */
24942     refresh : function(){
24943         Roo.log('refresh');
24944         var t = this.tpl;
24945         
24946         // if we are using something like 'domtemplate', then
24947         // the what gets used is:
24948         // t.applySubtemplate(NAME, data, wrapping data..)
24949         // the outer template then get' applied with
24950         //     the store 'extra data'
24951         // and the body get's added to the
24952         //      roo-name="data" node?
24953         //      <span class='roo-tpl-{name}'></span> ?????
24954         
24955         
24956         
24957         this.clearSelections();
24958         this.el.update("");
24959         var html = [];
24960         var records = this.store.getRange();
24961         if(records.length < 1) {
24962             
24963             // is this valid??  = should it render a template??
24964             
24965             this.el.update(this.emptyText);
24966             return;
24967         }
24968         var el = this.el;
24969         if (this.dataName) {
24970             this.el.update(t.apply(this.store.meta)); //????
24971             el = this.el.child('.roo-tpl-' + this.dataName);
24972         }
24973         
24974         for(var i = 0, len = records.length; i < len; i++){
24975             var data = this.prepareData(records[i].data, i, records[i]);
24976             this.fireEvent("preparedata", this, data, i, records[i]);
24977             html[html.length] = Roo.util.Format.trim(
24978                 this.dataName ?
24979                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24980                     t.apply(data)
24981             );
24982         }
24983         
24984         
24985         
24986         el.update(html.join(""));
24987         this.nodes = el.dom.childNodes;
24988         this.updateIndexes(0);
24989     },
24990     
24991
24992     /**
24993      * Function to override to reformat the data that is sent to
24994      * the template for each node.
24995      * DEPRICATED - use the preparedata event handler.
24996      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24997      * a JSON object for an UpdateManager bound view).
24998      */
24999     prepareData : function(data, index, record)
25000     {
25001         this.fireEvent("preparedata", this, data, index, record);
25002         return data;
25003     },
25004
25005     onUpdate : function(ds, record){
25006          Roo.log('on update');   
25007         this.clearSelections();
25008         var index = this.store.indexOf(record);
25009         var n = this.nodes[index];
25010         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25011         n.parentNode.removeChild(n);
25012         this.updateIndexes(index, index);
25013     },
25014
25015     
25016     
25017 // --------- FIXME     
25018     onAdd : function(ds, records, index)
25019     {
25020         Roo.log(['on Add', ds, records, index] );        
25021         this.clearSelections();
25022         if(this.nodes.length == 0){
25023             this.refresh();
25024             return;
25025         }
25026         var n = this.nodes[index];
25027         for(var i = 0, len = records.length; i < len; i++){
25028             var d = this.prepareData(records[i].data, i, records[i]);
25029             if(n){
25030                 this.tpl.insertBefore(n, d);
25031             }else{
25032                 
25033                 this.tpl.append(this.el, d);
25034             }
25035         }
25036         this.updateIndexes(index);
25037     },
25038
25039     onRemove : function(ds, record, index){
25040         Roo.log('onRemove');
25041         this.clearSelections();
25042         var el = this.dataName  ?
25043             this.el.child('.roo-tpl-' + this.dataName) :
25044             this.el; 
25045         
25046         el.dom.removeChild(this.nodes[index]);
25047         this.updateIndexes(index);
25048     },
25049
25050     /**
25051      * Refresh an individual node.
25052      * @param {Number} index
25053      */
25054     refreshNode : function(index){
25055         this.onUpdate(this.store, this.store.getAt(index));
25056     },
25057
25058     updateIndexes : function(startIndex, endIndex){
25059         var ns = this.nodes;
25060         startIndex = startIndex || 0;
25061         endIndex = endIndex || ns.length - 1;
25062         for(var i = startIndex; i <= endIndex; i++){
25063             ns[i].nodeIndex = i;
25064         }
25065     },
25066
25067     /**
25068      * Changes the data store this view uses and refresh the view.
25069      * @param {Store} store
25070      */
25071     setStore : function(store, initial){
25072         if(!initial && this.store){
25073             this.store.un("datachanged", this.refresh);
25074             this.store.un("add", this.onAdd);
25075             this.store.un("remove", this.onRemove);
25076             this.store.un("update", this.onUpdate);
25077             this.store.un("clear", this.refresh);
25078             this.store.un("beforeload", this.onBeforeLoad);
25079             this.store.un("load", this.onLoad);
25080             this.store.un("loadexception", this.onLoad);
25081         }
25082         if(store){
25083           
25084             store.on("datachanged", this.refresh, this);
25085             store.on("add", this.onAdd, this);
25086             store.on("remove", this.onRemove, this);
25087             store.on("update", this.onUpdate, this);
25088             store.on("clear", this.refresh, this);
25089             store.on("beforeload", this.onBeforeLoad, this);
25090             store.on("load", this.onLoad, this);
25091             store.on("loadexception", this.onLoad, this);
25092         }
25093         
25094         if(store){
25095             this.refresh();
25096         }
25097     },
25098     /**
25099      * onbeforeLoad - masks the loading area.
25100      *
25101      */
25102     onBeforeLoad : function(store,opts)
25103     {
25104          Roo.log('onBeforeLoad');   
25105         if (!opts.add) {
25106             this.el.update("");
25107         }
25108         this.el.mask(this.mask ? this.mask : "Loading" ); 
25109     },
25110     onLoad : function ()
25111     {
25112         this.el.unmask();
25113     },
25114     
25115
25116     /**
25117      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25118      * @param {HTMLElement} node
25119      * @return {HTMLElement} The template node
25120      */
25121     findItemFromChild : function(node){
25122         var el = this.dataName  ?
25123             this.el.child('.roo-tpl-' + this.dataName,true) :
25124             this.el.dom; 
25125         
25126         if(!node || node.parentNode == el){
25127                     return node;
25128             }
25129             var p = node.parentNode;
25130             while(p && p != el){
25131             if(p.parentNode == el){
25132                 return p;
25133             }
25134             p = p.parentNode;
25135         }
25136             return null;
25137     },
25138
25139     /** @ignore */
25140     onClick : function(e){
25141         var item = this.findItemFromChild(e.getTarget());
25142         if(item){
25143             var index = this.indexOf(item);
25144             if(this.onItemClick(item, index, e) !== false){
25145                 this.fireEvent("click", this, index, item, e);
25146             }
25147         }else{
25148             this.clearSelections();
25149         }
25150     },
25151
25152     /** @ignore */
25153     onContextMenu : function(e){
25154         var item = this.findItemFromChild(e.getTarget());
25155         if(item){
25156             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25157         }
25158     },
25159
25160     /** @ignore */
25161     onDblClick : function(e){
25162         var item = this.findItemFromChild(e.getTarget());
25163         if(item){
25164             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25165         }
25166     },
25167
25168     onItemClick : function(item, index, e)
25169     {
25170         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25171             return false;
25172         }
25173         if (this.toggleSelect) {
25174             var m = this.isSelected(item) ? 'unselect' : 'select';
25175             Roo.log(m);
25176             var _t = this;
25177             _t[m](item, true, false);
25178             return true;
25179         }
25180         if(this.multiSelect || this.singleSelect){
25181             if(this.multiSelect && e.shiftKey && this.lastSelection){
25182                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25183             }else{
25184                 this.select(item, this.multiSelect && e.ctrlKey);
25185                 this.lastSelection = item;
25186             }
25187             e.preventDefault();
25188         }
25189         return true;
25190     },
25191
25192     /**
25193      * Get the number of selected nodes.
25194      * @return {Number}
25195      */
25196     getSelectionCount : function(){
25197         return this.selections.length;
25198     },
25199
25200     /**
25201      * Get the currently selected nodes.
25202      * @return {Array} An array of HTMLElements
25203      */
25204     getSelectedNodes : function(){
25205         return this.selections;
25206     },
25207
25208     /**
25209      * Get the indexes of the selected nodes.
25210      * @return {Array}
25211      */
25212     getSelectedIndexes : function(){
25213         var indexes = [], s = this.selections;
25214         for(var i = 0, len = s.length; i < len; i++){
25215             indexes.push(s[i].nodeIndex);
25216         }
25217         return indexes;
25218     },
25219
25220     /**
25221      * Clear all selections
25222      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25223      */
25224     clearSelections : function(suppressEvent){
25225         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25226             this.cmp.elements = this.selections;
25227             this.cmp.removeClass(this.selectedClass);
25228             this.selections = [];
25229             if(!suppressEvent){
25230                 this.fireEvent("selectionchange", this, this.selections);
25231             }
25232         }
25233     },
25234
25235     /**
25236      * Returns true if the passed node is selected
25237      * @param {HTMLElement/Number} node The node or node index
25238      * @return {Boolean}
25239      */
25240     isSelected : function(node){
25241         var s = this.selections;
25242         if(s.length < 1){
25243             return false;
25244         }
25245         node = this.getNode(node);
25246         return s.indexOf(node) !== -1;
25247     },
25248
25249     /**
25250      * Selects nodes.
25251      * @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
25252      * @param {Boolean} keepExisting (optional) true to keep existing selections
25253      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25254      */
25255     select : function(nodeInfo, keepExisting, suppressEvent){
25256         if(nodeInfo instanceof Array){
25257             if(!keepExisting){
25258                 this.clearSelections(true);
25259             }
25260             for(var i = 0, len = nodeInfo.length; i < len; i++){
25261                 this.select(nodeInfo[i], true, true);
25262             }
25263             return;
25264         } 
25265         var node = this.getNode(nodeInfo);
25266         if(!node || this.isSelected(node)){
25267             return; // already selected.
25268         }
25269         if(!keepExisting){
25270             this.clearSelections(true);
25271         }
25272         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25273             Roo.fly(node).addClass(this.selectedClass);
25274             this.selections.push(node);
25275             if(!suppressEvent){
25276                 this.fireEvent("selectionchange", this, this.selections);
25277             }
25278         }
25279         
25280         
25281     },
25282       /**
25283      * Unselects nodes.
25284      * @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
25285      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25286      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25287      */
25288     unselect : function(nodeInfo, keepExisting, suppressEvent)
25289     {
25290         if(nodeInfo instanceof Array){
25291             Roo.each(this.selections, function(s) {
25292                 this.unselect(s, nodeInfo);
25293             }, this);
25294             return;
25295         }
25296         var node = this.getNode(nodeInfo);
25297         if(!node || !this.isSelected(node)){
25298             Roo.log("not selected");
25299             return; // not selected.
25300         }
25301         // fireevent???
25302         var ns = [];
25303         Roo.each(this.selections, function(s) {
25304             if (s == node ) {
25305                 Roo.fly(node).removeClass(this.selectedClass);
25306
25307                 return;
25308             }
25309             ns.push(s);
25310         },this);
25311         
25312         this.selections= ns;
25313         this.fireEvent("selectionchange", this, this.selections);
25314     },
25315
25316     /**
25317      * Gets a template node.
25318      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25319      * @return {HTMLElement} The node or null if it wasn't found
25320      */
25321     getNode : function(nodeInfo){
25322         if(typeof nodeInfo == "string"){
25323             return document.getElementById(nodeInfo);
25324         }else if(typeof nodeInfo == "number"){
25325             return this.nodes[nodeInfo];
25326         }
25327         return nodeInfo;
25328     },
25329
25330     /**
25331      * Gets a range template nodes.
25332      * @param {Number} startIndex
25333      * @param {Number} endIndex
25334      * @return {Array} An array of nodes
25335      */
25336     getNodes : function(start, end){
25337         var ns = this.nodes;
25338         start = start || 0;
25339         end = typeof end == "undefined" ? ns.length - 1 : end;
25340         var nodes = [];
25341         if(start <= end){
25342             for(var i = start; i <= end; i++){
25343                 nodes.push(ns[i]);
25344             }
25345         } else{
25346             for(var i = start; i >= end; i--){
25347                 nodes.push(ns[i]);
25348             }
25349         }
25350         return nodes;
25351     },
25352
25353     /**
25354      * Finds the index of the passed node
25355      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25356      * @return {Number} The index of the node or -1
25357      */
25358     indexOf : function(node){
25359         node = this.getNode(node);
25360         if(typeof node.nodeIndex == "number"){
25361             return node.nodeIndex;
25362         }
25363         var ns = this.nodes;
25364         for(var i = 0, len = ns.length; i < len; i++){
25365             if(ns[i] == node){
25366                 return i;
25367             }
25368         }
25369         return -1;
25370     }
25371 });
25372 /*
25373  * Based on:
25374  * Ext JS Library 1.1.1
25375  * Copyright(c) 2006-2007, Ext JS, LLC.
25376  *
25377  * Originally Released Under LGPL - original licence link has changed is not relivant.
25378  *
25379  * Fork - LGPL
25380  * <script type="text/javascript">
25381  */
25382
25383 /**
25384  * @class Roo.JsonView
25385  * @extends Roo.View
25386  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25387 <pre><code>
25388 var view = new Roo.JsonView({
25389     container: "my-element",
25390     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25391     multiSelect: true, 
25392     jsonRoot: "data" 
25393 });
25394
25395 // listen for node click?
25396 view.on("click", function(vw, index, node, e){
25397     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25398 });
25399
25400 // direct load of JSON data
25401 view.load("foobar.php");
25402
25403 // Example from my blog list
25404 var tpl = new Roo.Template(
25405     '&lt;div class="entry"&gt;' +
25406     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25407     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25408     "&lt;/div&gt;&lt;hr /&gt;"
25409 );
25410
25411 var moreView = new Roo.JsonView({
25412     container :  "entry-list", 
25413     template : tpl,
25414     jsonRoot: "posts"
25415 });
25416 moreView.on("beforerender", this.sortEntries, this);
25417 moreView.load({
25418     url: "/blog/get-posts.php",
25419     params: "allposts=true",
25420     text: "Loading Blog Entries..."
25421 });
25422 </code></pre>
25423
25424 * Note: old code is supported with arguments : (container, template, config)
25425
25426
25427  * @constructor
25428  * Create a new JsonView
25429  * 
25430  * @param {Object} config The config object
25431  * 
25432  */
25433 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25434     
25435     
25436     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25437
25438     var um = this.el.getUpdateManager();
25439     um.setRenderer(this);
25440     um.on("update", this.onLoad, this);
25441     um.on("failure", this.onLoadException, this);
25442
25443     /**
25444      * @event beforerender
25445      * Fires before rendering of the downloaded JSON data.
25446      * @param {Roo.JsonView} this
25447      * @param {Object} data The JSON data loaded
25448      */
25449     /**
25450      * @event load
25451      * Fires when data is loaded.
25452      * @param {Roo.JsonView} this
25453      * @param {Object} data The JSON data loaded
25454      * @param {Object} response The raw Connect response object
25455      */
25456     /**
25457      * @event loadexception
25458      * Fires when loading fails.
25459      * @param {Roo.JsonView} this
25460      * @param {Object} response The raw Connect response object
25461      */
25462     this.addEvents({
25463         'beforerender' : true,
25464         'load' : true,
25465         'loadexception' : true
25466     });
25467 };
25468 Roo.extend(Roo.JsonView, Roo.View, {
25469     /**
25470      * @type {String} The root property in the loaded JSON object that contains the data
25471      */
25472     jsonRoot : "",
25473
25474     /**
25475      * Refreshes the view.
25476      */
25477     refresh : function(){
25478         this.clearSelections();
25479         this.el.update("");
25480         var html = [];
25481         var o = this.jsonData;
25482         if(o && o.length > 0){
25483             for(var i = 0, len = o.length; i < len; i++){
25484                 var data = this.prepareData(o[i], i, o);
25485                 html[html.length] = this.tpl.apply(data);
25486             }
25487         }else{
25488             html.push(this.emptyText);
25489         }
25490         this.el.update(html.join(""));
25491         this.nodes = this.el.dom.childNodes;
25492         this.updateIndexes(0);
25493     },
25494
25495     /**
25496      * 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.
25497      * @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:
25498      <pre><code>
25499      view.load({
25500          url: "your-url.php",
25501          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25502          callback: yourFunction,
25503          scope: yourObject, //(optional scope)
25504          discardUrl: false,
25505          nocache: false,
25506          text: "Loading...",
25507          timeout: 30,
25508          scripts: false
25509      });
25510      </code></pre>
25511      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25512      * 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.
25513      * @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}
25514      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25515      * @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.
25516      */
25517     load : function(){
25518         var um = this.el.getUpdateManager();
25519         um.update.apply(um, arguments);
25520     },
25521
25522     render : function(el, response){
25523         this.clearSelections();
25524         this.el.update("");
25525         var o;
25526         try{
25527             o = Roo.util.JSON.decode(response.responseText);
25528             if(this.jsonRoot){
25529                 
25530                 o = o[this.jsonRoot];
25531             }
25532         } catch(e){
25533         }
25534         /**
25535          * The current JSON data or null
25536          */
25537         this.jsonData = o;
25538         this.beforeRender();
25539         this.refresh();
25540     },
25541
25542 /**
25543  * Get the number of records in the current JSON dataset
25544  * @return {Number}
25545  */
25546     getCount : function(){
25547         return this.jsonData ? this.jsonData.length : 0;
25548     },
25549
25550 /**
25551  * Returns the JSON object for the specified node(s)
25552  * @param {HTMLElement/Array} node The node or an array of nodes
25553  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25554  * you get the JSON object for the node
25555  */
25556     getNodeData : function(node){
25557         if(node instanceof Array){
25558             var data = [];
25559             for(var i = 0, len = node.length; i < len; i++){
25560                 data.push(this.getNodeData(node[i]));
25561             }
25562             return data;
25563         }
25564         return this.jsonData[this.indexOf(node)] || null;
25565     },
25566
25567     beforeRender : function(){
25568         this.snapshot = this.jsonData;
25569         if(this.sortInfo){
25570             this.sort.apply(this, this.sortInfo);
25571         }
25572         this.fireEvent("beforerender", this, this.jsonData);
25573     },
25574
25575     onLoad : function(el, o){
25576         this.fireEvent("load", this, this.jsonData, o);
25577     },
25578
25579     onLoadException : function(el, o){
25580         this.fireEvent("loadexception", this, o);
25581     },
25582
25583 /**
25584  * Filter the data by a specific property.
25585  * @param {String} property A property on your JSON objects
25586  * @param {String/RegExp} value Either string that the property values
25587  * should start with, or a RegExp to test against the property
25588  */
25589     filter : function(property, value){
25590         if(this.jsonData){
25591             var data = [];
25592             var ss = this.snapshot;
25593             if(typeof value == "string"){
25594                 var vlen = value.length;
25595                 if(vlen == 0){
25596                     this.clearFilter();
25597                     return;
25598                 }
25599                 value = value.toLowerCase();
25600                 for(var i = 0, len = ss.length; i < len; i++){
25601                     var o = ss[i];
25602                     if(o[property].substr(0, vlen).toLowerCase() == value){
25603                         data.push(o);
25604                     }
25605                 }
25606             } else if(value.exec){ // regex?
25607                 for(var i = 0, len = ss.length; i < len; i++){
25608                     var o = ss[i];
25609                     if(value.test(o[property])){
25610                         data.push(o);
25611                     }
25612                 }
25613             } else{
25614                 return;
25615             }
25616             this.jsonData = data;
25617             this.refresh();
25618         }
25619     },
25620
25621 /**
25622  * Filter by a function. The passed function will be called with each
25623  * object in the current dataset. If the function returns true the value is kept,
25624  * otherwise it is filtered.
25625  * @param {Function} fn
25626  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25627  */
25628     filterBy : function(fn, scope){
25629         if(this.jsonData){
25630             var data = [];
25631             var ss = this.snapshot;
25632             for(var i = 0, len = ss.length; i < len; i++){
25633                 var o = ss[i];
25634                 if(fn.call(scope || this, o)){
25635                     data.push(o);
25636                 }
25637             }
25638             this.jsonData = data;
25639             this.refresh();
25640         }
25641     },
25642
25643 /**
25644  * Clears the current filter.
25645  */
25646     clearFilter : function(){
25647         if(this.snapshot && this.jsonData != this.snapshot){
25648             this.jsonData = this.snapshot;
25649             this.refresh();
25650         }
25651     },
25652
25653
25654 /**
25655  * Sorts the data for this view and refreshes it.
25656  * @param {String} property A property on your JSON objects to sort on
25657  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25658  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25659  */
25660     sort : function(property, dir, sortType){
25661         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25662         if(this.jsonData){
25663             var p = property;
25664             var dsc = dir && dir.toLowerCase() == "desc";
25665             var f = function(o1, o2){
25666                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25667                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25668                 ;
25669                 if(v1 < v2){
25670                     return dsc ? +1 : -1;
25671                 } else if(v1 > v2){
25672                     return dsc ? -1 : +1;
25673                 } else{
25674                     return 0;
25675                 }
25676             };
25677             this.jsonData.sort(f);
25678             this.refresh();
25679             if(this.jsonData != this.snapshot){
25680                 this.snapshot.sort(f);
25681             }
25682         }
25683     }
25684 });/*
25685  * Based on:
25686  * Ext JS Library 1.1.1
25687  * Copyright(c) 2006-2007, Ext JS, LLC.
25688  *
25689  * Originally Released Under LGPL - original licence link has changed is not relivant.
25690  *
25691  * Fork - LGPL
25692  * <script type="text/javascript">
25693  */
25694  
25695
25696 /**
25697  * @class Roo.ColorPalette
25698  * @extends Roo.Component
25699  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25700  * Here's an example of typical usage:
25701  * <pre><code>
25702 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25703 cp.render('my-div');
25704
25705 cp.on('select', function(palette, selColor){
25706     // do something with selColor
25707 });
25708 </code></pre>
25709  * @constructor
25710  * Create a new ColorPalette
25711  * @param {Object} config The config object
25712  */
25713 Roo.ColorPalette = function(config){
25714     Roo.ColorPalette.superclass.constructor.call(this, config);
25715     this.addEvents({
25716         /**
25717              * @event select
25718              * Fires when a color is selected
25719              * @param {ColorPalette} this
25720              * @param {String} color The 6-digit color hex code (without the # symbol)
25721              */
25722         select: true
25723     });
25724
25725     if(this.handler){
25726         this.on("select", this.handler, this.scope, true);
25727     }
25728 };
25729 Roo.extend(Roo.ColorPalette, Roo.Component, {
25730     /**
25731      * @cfg {String} itemCls
25732      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25733      */
25734     itemCls : "x-color-palette",
25735     /**
25736      * @cfg {String} value
25737      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25738      * the hex codes are case-sensitive.
25739      */
25740     value : null,
25741     clickEvent:'click',
25742     // private
25743     ctype: "Roo.ColorPalette",
25744
25745     /**
25746      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25747      */
25748     allowReselect : false,
25749
25750     /**
25751      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25752      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25753      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25754      * of colors with the width setting until the box is symmetrical.</p>
25755      * <p>You can override individual colors if needed:</p>
25756      * <pre><code>
25757 var cp = new Roo.ColorPalette();
25758 cp.colors[0] = "FF0000";  // change the first box to red
25759 </code></pre>
25760
25761 Or you can provide a custom array of your own for complete control:
25762 <pre><code>
25763 var cp = new Roo.ColorPalette();
25764 cp.colors = ["000000", "993300", "333300"];
25765 </code></pre>
25766      * @type Array
25767      */
25768     colors : [
25769         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25770         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25771         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25772         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25773         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25774     ],
25775
25776     // private
25777     onRender : function(container, position){
25778         var t = new Roo.MasterTemplate(
25779             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25780         );
25781         var c = this.colors;
25782         for(var i = 0, len = c.length; i < len; i++){
25783             t.add([c[i]]);
25784         }
25785         var el = document.createElement("div");
25786         el.className = this.itemCls;
25787         t.overwrite(el);
25788         container.dom.insertBefore(el, position);
25789         this.el = Roo.get(el);
25790         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25791         if(this.clickEvent != 'click'){
25792             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25793         }
25794     },
25795
25796     // private
25797     afterRender : function(){
25798         Roo.ColorPalette.superclass.afterRender.call(this);
25799         if(this.value){
25800             var s = this.value;
25801             this.value = null;
25802             this.select(s);
25803         }
25804     },
25805
25806     // private
25807     handleClick : function(e, t){
25808         e.preventDefault();
25809         if(!this.disabled){
25810             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25811             this.select(c.toUpperCase());
25812         }
25813     },
25814
25815     /**
25816      * Selects the specified color in the palette (fires the select event)
25817      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25818      */
25819     select : function(color){
25820         color = color.replace("#", "");
25821         if(color != this.value || this.allowReselect){
25822             var el = this.el;
25823             if(this.value){
25824                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25825             }
25826             el.child("a.color-"+color).addClass("x-color-palette-sel");
25827             this.value = color;
25828             this.fireEvent("select", this, color);
25829         }
25830     }
25831 });/*
25832  * Based on:
25833  * Ext JS Library 1.1.1
25834  * Copyright(c) 2006-2007, Ext JS, LLC.
25835  *
25836  * Originally Released Under LGPL - original licence link has changed is not relivant.
25837  *
25838  * Fork - LGPL
25839  * <script type="text/javascript">
25840  */
25841  
25842 /**
25843  * @class Roo.DatePicker
25844  * @extends Roo.Component
25845  * Simple date picker class.
25846  * @constructor
25847  * Create a new DatePicker
25848  * @param {Object} config The config object
25849  */
25850 Roo.DatePicker = function(config){
25851     Roo.DatePicker.superclass.constructor.call(this, config);
25852
25853     this.value = config && config.value ?
25854                  config.value.clearTime() : new Date().clearTime();
25855
25856     this.addEvents({
25857         /**
25858              * @event select
25859              * Fires when a date is selected
25860              * @param {DatePicker} this
25861              * @param {Date} date The selected date
25862              */
25863         'select': true,
25864         /**
25865              * @event monthchange
25866              * Fires when the displayed month changes 
25867              * @param {DatePicker} this
25868              * @param {Date} date The selected month
25869              */
25870         'monthchange': true
25871     });
25872
25873     if(this.handler){
25874         this.on("select", this.handler,  this.scope || this);
25875     }
25876     // build the disabledDatesRE
25877     if(!this.disabledDatesRE && this.disabledDates){
25878         var dd = this.disabledDates;
25879         var re = "(?:";
25880         for(var i = 0; i < dd.length; i++){
25881             re += dd[i];
25882             if(i != dd.length-1) re += "|";
25883         }
25884         this.disabledDatesRE = new RegExp(re + ")");
25885     }
25886 };
25887
25888 Roo.extend(Roo.DatePicker, Roo.Component, {
25889     /**
25890      * @cfg {String} todayText
25891      * The text to display on the button that selects the current date (defaults to "Today")
25892      */
25893     todayText : "Today",
25894     /**
25895      * @cfg {String} okText
25896      * The text to display on the ok button
25897      */
25898     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25899     /**
25900      * @cfg {String} cancelText
25901      * The text to display on the cancel button
25902      */
25903     cancelText : "Cancel",
25904     /**
25905      * @cfg {String} todayTip
25906      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25907      */
25908     todayTip : "{0} (Spacebar)",
25909     /**
25910      * @cfg {Date} minDate
25911      * Minimum allowable date (JavaScript date object, defaults to null)
25912      */
25913     minDate : null,
25914     /**
25915      * @cfg {Date} maxDate
25916      * Maximum allowable date (JavaScript date object, defaults to null)
25917      */
25918     maxDate : null,
25919     /**
25920      * @cfg {String} minText
25921      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25922      */
25923     minText : "This date is before the minimum date",
25924     /**
25925      * @cfg {String} maxText
25926      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25927      */
25928     maxText : "This date is after the maximum date",
25929     /**
25930      * @cfg {String} format
25931      * The default date format string which can be overriden for localization support.  The format must be
25932      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25933      */
25934     format : "m/d/y",
25935     /**
25936      * @cfg {Array} disabledDays
25937      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25938      */
25939     disabledDays : null,
25940     /**
25941      * @cfg {String} disabledDaysText
25942      * The tooltip to display when the date falls on a disabled day (defaults to "")
25943      */
25944     disabledDaysText : "",
25945     /**
25946      * @cfg {RegExp} disabledDatesRE
25947      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25948      */
25949     disabledDatesRE : null,
25950     /**
25951      * @cfg {String} disabledDatesText
25952      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25953      */
25954     disabledDatesText : "",
25955     /**
25956      * @cfg {Boolean} constrainToViewport
25957      * True to constrain the date picker to the viewport (defaults to true)
25958      */
25959     constrainToViewport : true,
25960     /**
25961      * @cfg {Array} monthNames
25962      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25963      */
25964     monthNames : Date.monthNames,
25965     /**
25966      * @cfg {Array} dayNames
25967      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25968      */
25969     dayNames : Date.dayNames,
25970     /**
25971      * @cfg {String} nextText
25972      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25973      */
25974     nextText: 'Next Month (Control+Right)',
25975     /**
25976      * @cfg {String} prevText
25977      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25978      */
25979     prevText: 'Previous Month (Control+Left)',
25980     /**
25981      * @cfg {String} monthYearText
25982      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25983      */
25984     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25985     /**
25986      * @cfg {Number} startDay
25987      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25988      */
25989     startDay : 0,
25990     /**
25991      * @cfg {Bool} showClear
25992      * Show a clear button (usefull for date form elements that can be blank.)
25993      */
25994     
25995     showClear: false,
25996     
25997     /**
25998      * Sets the value of the date field
25999      * @param {Date} value The date to set
26000      */
26001     setValue : function(value){
26002         var old = this.value;
26003         
26004         if (typeof(value) == 'string') {
26005          
26006             value = Date.parseDate(value, this.format);
26007         }
26008         if (!value) {
26009             value = new Date();
26010         }
26011         
26012         this.value = value.clearTime(true);
26013         if(this.el){
26014             this.update(this.value);
26015         }
26016     },
26017
26018     /**
26019      * Gets the current selected value of the date field
26020      * @return {Date} The selected date
26021      */
26022     getValue : function(){
26023         return this.value;
26024     },
26025
26026     // private
26027     focus : function(){
26028         if(this.el){
26029             this.update(this.activeDate);
26030         }
26031     },
26032
26033     // privateval
26034     onRender : function(container, position){
26035         
26036         var m = [
26037              '<table cellspacing="0">',
26038                 '<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>',
26039                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26040         var dn = this.dayNames;
26041         for(var i = 0; i < 7; i++){
26042             var d = this.startDay+i;
26043             if(d > 6){
26044                 d = d-7;
26045             }
26046             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26047         }
26048         m[m.length] = "</tr></thead><tbody><tr>";
26049         for(var i = 0; i < 42; i++) {
26050             if(i % 7 == 0 && i != 0){
26051                 m[m.length] = "</tr><tr>";
26052             }
26053             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26054         }
26055         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26056             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26057
26058         var el = document.createElement("div");
26059         el.className = "x-date-picker";
26060         el.innerHTML = m.join("");
26061
26062         container.dom.insertBefore(el, position);
26063
26064         this.el = Roo.get(el);
26065         this.eventEl = Roo.get(el.firstChild);
26066
26067         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26068             handler: this.showPrevMonth,
26069             scope: this,
26070             preventDefault:true,
26071             stopDefault:true
26072         });
26073
26074         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26075             handler: this.showNextMonth,
26076             scope: this,
26077             preventDefault:true,
26078             stopDefault:true
26079         });
26080
26081         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26082
26083         this.monthPicker = this.el.down('div.x-date-mp');
26084         this.monthPicker.enableDisplayMode('block');
26085         
26086         var kn = new Roo.KeyNav(this.eventEl, {
26087             "left" : function(e){
26088                 e.ctrlKey ?
26089                     this.showPrevMonth() :
26090                     this.update(this.activeDate.add("d", -1));
26091             },
26092
26093             "right" : function(e){
26094                 e.ctrlKey ?
26095                     this.showNextMonth() :
26096                     this.update(this.activeDate.add("d", 1));
26097             },
26098
26099             "up" : function(e){
26100                 e.ctrlKey ?
26101                     this.showNextYear() :
26102                     this.update(this.activeDate.add("d", -7));
26103             },
26104
26105             "down" : function(e){
26106                 e.ctrlKey ?
26107                     this.showPrevYear() :
26108                     this.update(this.activeDate.add("d", 7));
26109             },
26110
26111             "pageUp" : function(e){
26112                 this.showNextMonth();
26113             },
26114
26115             "pageDown" : function(e){
26116                 this.showPrevMonth();
26117             },
26118
26119             "enter" : function(e){
26120                 e.stopPropagation();
26121                 return true;
26122             },
26123
26124             scope : this
26125         });
26126
26127         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26128
26129         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26130
26131         this.el.unselectable();
26132         
26133         this.cells = this.el.select("table.x-date-inner tbody td");
26134         this.textNodes = this.el.query("table.x-date-inner tbody span");
26135
26136         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26137             text: "&#160;",
26138             tooltip: this.monthYearText
26139         });
26140
26141         this.mbtn.on('click', this.showMonthPicker, this);
26142         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26143
26144
26145         var today = (new Date()).dateFormat(this.format);
26146         
26147         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26148         if (this.showClear) {
26149             baseTb.add( new Roo.Toolbar.Fill());
26150         }
26151         baseTb.add({
26152             text: String.format(this.todayText, today),
26153             tooltip: String.format(this.todayTip, today),
26154             handler: this.selectToday,
26155             scope: this
26156         });
26157         
26158         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26159             
26160         //});
26161         if (this.showClear) {
26162             
26163             baseTb.add( new Roo.Toolbar.Fill());
26164             baseTb.add({
26165                 text: '&#160;',
26166                 cls: 'x-btn-icon x-btn-clear',
26167                 handler: function() {
26168                     //this.value = '';
26169                     this.fireEvent("select", this, '');
26170                 },
26171                 scope: this
26172             });
26173         }
26174         
26175         
26176         if(Roo.isIE){
26177             this.el.repaint();
26178         }
26179         this.update(this.value);
26180     },
26181
26182     createMonthPicker : function(){
26183         if(!this.monthPicker.dom.firstChild){
26184             var buf = ['<table border="0" cellspacing="0">'];
26185             for(var i = 0; i < 6; i++){
26186                 buf.push(
26187                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26188                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26189                     i == 0 ?
26190                     '<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>' :
26191                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26192                 );
26193             }
26194             buf.push(
26195                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26196                     this.okText,
26197                     '</button><button type="button" class="x-date-mp-cancel">',
26198                     this.cancelText,
26199                     '</button></td></tr>',
26200                 '</table>'
26201             );
26202             this.monthPicker.update(buf.join(''));
26203             this.monthPicker.on('click', this.onMonthClick, this);
26204             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26205
26206             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26207             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26208
26209             this.mpMonths.each(function(m, a, i){
26210                 i += 1;
26211                 if((i%2) == 0){
26212                     m.dom.xmonth = 5 + Math.round(i * .5);
26213                 }else{
26214                     m.dom.xmonth = Math.round((i-1) * .5);
26215                 }
26216             });
26217         }
26218     },
26219
26220     showMonthPicker : function(){
26221         this.createMonthPicker();
26222         var size = this.el.getSize();
26223         this.monthPicker.setSize(size);
26224         this.monthPicker.child('table').setSize(size);
26225
26226         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26227         this.updateMPMonth(this.mpSelMonth);
26228         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26229         this.updateMPYear(this.mpSelYear);
26230
26231         this.monthPicker.slideIn('t', {duration:.2});
26232     },
26233
26234     updateMPYear : function(y){
26235         this.mpyear = y;
26236         var ys = this.mpYears.elements;
26237         for(var i = 1; i <= 10; i++){
26238             var td = ys[i-1], y2;
26239             if((i%2) == 0){
26240                 y2 = y + Math.round(i * .5);
26241                 td.firstChild.innerHTML = y2;
26242                 td.xyear = y2;
26243             }else{
26244                 y2 = y - (5-Math.round(i * .5));
26245                 td.firstChild.innerHTML = y2;
26246                 td.xyear = y2;
26247             }
26248             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26249         }
26250     },
26251
26252     updateMPMonth : function(sm){
26253         this.mpMonths.each(function(m, a, i){
26254             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26255         });
26256     },
26257
26258     selectMPMonth: function(m){
26259         
26260     },
26261
26262     onMonthClick : function(e, t){
26263         e.stopEvent();
26264         var el = new Roo.Element(t), pn;
26265         if(el.is('button.x-date-mp-cancel')){
26266             this.hideMonthPicker();
26267         }
26268         else if(el.is('button.x-date-mp-ok')){
26269             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26270             this.hideMonthPicker();
26271         }
26272         else if(pn = el.up('td.x-date-mp-month', 2)){
26273             this.mpMonths.removeClass('x-date-mp-sel');
26274             pn.addClass('x-date-mp-sel');
26275             this.mpSelMonth = pn.dom.xmonth;
26276         }
26277         else if(pn = el.up('td.x-date-mp-year', 2)){
26278             this.mpYears.removeClass('x-date-mp-sel');
26279             pn.addClass('x-date-mp-sel');
26280             this.mpSelYear = pn.dom.xyear;
26281         }
26282         else if(el.is('a.x-date-mp-prev')){
26283             this.updateMPYear(this.mpyear-10);
26284         }
26285         else if(el.is('a.x-date-mp-next')){
26286             this.updateMPYear(this.mpyear+10);
26287         }
26288     },
26289
26290     onMonthDblClick : function(e, t){
26291         e.stopEvent();
26292         var el = new Roo.Element(t), pn;
26293         if(pn = el.up('td.x-date-mp-month', 2)){
26294             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26295             this.hideMonthPicker();
26296         }
26297         else if(pn = el.up('td.x-date-mp-year', 2)){
26298             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26299             this.hideMonthPicker();
26300         }
26301     },
26302
26303     hideMonthPicker : function(disableAnim){
26304         if(this.monthPicker){
26305             if(disableAnim === true){
26306                 this.monthPicker.hide();
26307             }else{
26308                 this.monthPicker.slideOut('t', {duration:.2});
26309             }
26310         }
26311     },
26312
26313     // private
26314     showPrevMonth : function(e){
26315         this.update(this.activeDate.add("mo", -1));
26316     },
26317
26318     // private
26319     showNextMonth : function(e){
26320         this.update(this.activeDate.add("mo", 1));
26321     },
26322
26323     // private
26324     showPrevYear : function(){
26325         this.update(this.activeDate.add("y", -1));
26326     },
26327
26328     // private
26329     showNextYear : function(){
26330         this.update(this.activeDate.add("y", 1));
26331     },
26332
26333     // private
26334     handleMouseWheel : function(e){
26335         var delta = e.getWheelDelta();
26336         if(delta > 0){
26337             this.showPrevMonth();
26338             e.stopEvent();
26339         } else if(delta < 0){
26340             this.showNextMonth();
26341             e.stopEvent();
26342         }
26343     },
26344
26345     // private
26346     handleDateClick : function(e, t){
26347         e.stopEvent();
26348         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26349             this.setValue(new Date(t.dateValue));
26350             this.fireEvent("select", this, this.value);
26351         }
26352     },
26353
26354     // private
26355     selectToday : function(){
26356         this.setValue(new Date().clearTime());
26357         this.fireEvent("select", this, this.value);
26358     },
26359
26360     // private
26361     update : function(date)
26362     {
26363         var vd = this.activeDate;
26364         this.activeDate = date;
26365         if(vd && this.el){
26366             var t = date.getTime();
26367             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26368                 this.cells.removeClass("x-date-selected");
26369                 this.cells.each(function(c){
26370                    if(c.dom.firstChild.dateValue == t){
26371                        c.addClass("x-date-selected");
26372                        setTimeout(function(){
26373                             try{c.dom.firstChild.focus();}catch(e){}
26374                        }, 50);
26375                        return false;
26376                    }
26377                 });
26378                 return;
26379             }
26380         }
26381         
26382         var days = date.getDaysInMonth();
26383         var firstOfMonth = date.getFirstDateOfMonth();
26384         var startingPos = firstOfMonth.getDay()-this.startDay;
26385
26386         if(startingPos <= this.startDay){
26387             startingPos += 7;
26388         }
26389
26390         var pm = date.add("mo", -1);
26391         var prevStart = pm.getDaysInMonth()-startingPos;
26392
26393         var cells = this.cells.elements;
26394         var textEls = this.textNodes;
26395         days += startingPos;
26396
26397         // convert everything to numbers so it's fast
26398         var day = 86400000;
26399         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26400         var today = new Date().clearTime().getTime();
26401         var sel = date.clearTime().getTime();
26402         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26403         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26404         var ddMatch = this.disabledDatesRE;
26405         var ddText = this.disabledDatesText;
26406         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26407         var ddaysText = this.disabledDaysText;
26408         var format = this.format;
26409
26410         var setCellClass = function(cal, cell){
26411             cell.title = "";
26412             var t = d.getTime();
26413             cell.firstChild.dateValue = t;
26414             if(t == today){
26415                 cell.className += " x-date-today";
26416                 cell.title = cal.todayText;
26417             }
26418             if(t == sel){
26419                 cell.className += " x-date-selected";
26420                 setTimeout(function(){
26421                     try{cell.firstChild.focus();}catch(e){}
26422                 }, 50);
26423             }
26424             // disabling
26425             if(t < min) {
26426                 cell.className = " x-date-disabled";
26427                 cell.title = cal.minText;
26428                 return;
26429             }
26430             if(t > max) {
26431                 cell.className = " x-date-disabled";
26432                 cell.title = cal.maxText;
26433                 return;
26434             }
26435             if(ddays){
26436                 if(ddays.indexOf(d.getDay()) != -1){
26437                     cell.title = ddaysText;
26438                     cell.className = " x-date-disabled";
26439                 }
26440             }
26441             if(ddMatch && format){
26442                 var fvalue = d.dateFormat(format);
26443                 if(ddMatch.test(fvalue)){
26444                     cell.title = ddText.replace("%0", fvalue);
26445                     cell.className = " x-date-disabled";
26446                 }
26447             }
26448         };
26449
26450         var i = 0;
26451         for(; i < startingPos; i++) {
26452             textEls[i].innerHTML = (++prevStart);
26453             d.setDate(d.getDate()+1);
26454             cells[i].className = "x-date-prevday";
26455             setCellClass(this, cells[i]);
26456         }
26457         for(; i < days; i++){
26458             intDay = i - startingPos + 1;
26459             textEls[i].innerHTML = (intDay);
26460             d.setDate(d.getDate()+1);
26461             cells[i].className = "x-date-active";
26462             setCellClass(this, cells[i]);
26463         }
26464         var extraDays = 0;
26465         for(; i < 42; i++) {
26466              textEls[i].innerHTML = (++extraDays);
26467              d.setDate(d.getDate()+1);
26468              cells[i].className = "x-date-nextday";
26469              setCellClass(this, cells[i]);
26470         }
26471
26472         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26473         this.fireEvent('monthchange', this, date);
26474         
26475         if(!this.internalRender){
26476             var main = this.el.dom.firstChild;
26477             var w = main.offsetWidth;
26478             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26479             Roo.fly(main).setWidth(w);
26480             this.internalRender = true;
26481             // opera does not respect the auto grow header center column
26482             // then, after it gets a width opera refuses to recalculate
26483             // without a second pass
26484             if(Roo.isOpera && !this.secondPass){
26485                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26486                 this.secondPass = true;
26487                 this.update.defer(10, this, [date]);
26488             }
26489         }
26490         
26491         
26492     }
26493 });        /*
26494  * Based on:
26495  * Ext JS Library 1.1.1
26496  * Copyright(c) 2006-2007, Ext JS, LLC.
26497  *
26498  * Originally Released Under LGPL - original licence link has changed is not relivant.
26499  *
26500  * Fork - LGPL
26501  * <script type="text/javascript">
26502  */
26503 /**
26504  * @class Roo.TabPanel
26505  * @extends Roo.util.Observable
26506  * A lightweight tab container.
26507  * <br><br>
26508  * Usage:
26509  * <pre><code>
26510 // basic tabs 1, built from existing content
26511 var tabs = new Roo.TabPanel("tabs1");
26512 tabs.addTab("script", "View Script");
26513 tabs.addTab("markup", "View Markup");
26514 tabs.activate("script");
26515
26516 // more advanced tabs, built from javascript
26517 var jtabs = new Roo.TabPanel("jtabs");
26518 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26519
26520 // set up the UpdateManager
26521 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26522 var updater = tab2.getUpdateManager();
26523 updater.setDefaultUrl("ajax1.htm");
26524 tab2.on('activate', updater.refresh, updater, true);
26525
26526 // Use setUrl for Ajax loading
26527 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26528 tab3.setUrl("ajax2.htm", null, true);
26529
26530 // Disabled tab
26531 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26532 tab4.disable();
26533
26534 jtabs.activate("jtabs-1");
26535  * </code></pre>
26536  * @constructor
26537  * Create a new TabPanel.
26538  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26539  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26540  */
26541 Roo.TabPanel = function(container, config){
26542     /**
26543     * The container element for this TabPanel.
26544     * @type Roo.Element
26545     */
26546     this.el = Roo.get(container, true);
26547     if(config){
26548         if(typeof config == "boolean"){
26549             this.tabPosition = config ? "bottom" : "top";
26550         }else{
26551             Roo.apply(this, config);
26552         }
26553     }
26554     if(this.tabPosition == "bottom"){
26555         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26556         this.el.addClass("x-tabs-bottom");
26557     }
26558     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26559     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26560     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26561     if(Roo.isIE){
26562         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26563     }
26564     if(this.tabPosition != "bottom"){
26565         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26566          * @type Roo.Element
26567          */
26568         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26569         this.el.addClass("x-tabs-top");
26570     }
26571     this.items = [];
26572
26573     this.bodyEl.setStyle("position", "relative");
26574
26575     this.active = null;
26576     this.activateDelegate = this.activate.createDelegate(this);
26577
26578     this.addEvents({
26579         /**
26580          * @event tabchange
26581          * Fires when the active tab changes
26582          * @param {Roo.TabPanel} this
26583          * @param {Roo.TabPanelItem} activePanel The new active tab
26584          */
26585         "tabchange": true,
26586         /**
26587          * @event beforetabchange
26588          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26589          * @param {Roo.TabPanel} this
26590          * @param {Object} e Set cancel to true on this object to cancel the tab change
26591          * @param {Roo.TabPanelItem} tab The tab being changed to
26592          */
26593         "beforetabchange" : true
26594     });
26595
26596     Roo.EventManager.onWindowResize(this.onResize, this);
26597     this.cpad = this.el.getPadding("lr");
26598     this.hiddenCount = 0;
26599
26600
26601     // toolbar on the tabbar support...
26602     if (this.toolbar) {
26603         var tcfg = this.toolbar;
26604         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26605         this.toolbar = new Roo.Toolbar(tcfg);
26606         if (Roo.isSafari) {
26607             var tbl = tcfg.container.child('table', true);
26608             tbl.setAttribute('width', '100%');
26609         }
26610         
26611     }
26612    
26613
26614
26615     Roo.TabPanel.superclass.constructor.call(this);
26616 };
26617
26618 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26619     /*
26620      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26621      */
26622     tabPosition : "top",
26623     /*
26624      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26625      */
26626     currentTabWidth : 0,
26627     /*
26628      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26629      */
26630     minTabWidth : 40,
26631     /*
26632      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26633      */
26634     maxTabWidth : 250,
26635     /*
26636      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26637      */
26638     preferredTabWidth : 175,
26639     /*
26640      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26641      */
26642     resizeTabs : false,
26643     /*
26644      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26645      */
26646     monitorResize : true,
26647     /*
26648      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26649      */
26650     toolbar : false,
26651
26652     /**
26653      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26654      * @param {String} id The id of the div to use <b>or create</b>
26655      * @param {String} text The text for the tab
26656      * @param {String} content (optional) Content to put in the TabPanelItem body
26657      * @param {Boolean} closable (optional) True to create a close icon on the tab
26658      * @return {Roo.TabPanelItem} The created TabPanelItem
26659      */
26660     addTab : function(id, text, content, closable){
26661         var item = new Roo.TabPanelItem(this, id, text, closable);
26662         this.addTabItem(item);
26663         if(content){
26664             item.setContent(content);
26665         }
26666         return item;
26667     },
26668
26669     /**
26670      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26671      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26672      * @return {Roo.TabPanelItem}
26673      */
26674     getTab : function(id){
26675         return this.items[id];
26676     },
26677
26678     /**
26679      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26680      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26681      */
26682     hideTab : function(id){
26683         var t = this.items[id];
26684         if(!t.isHidden()){
26685            t.setHidden(true);
26686            this.hiddenCount++;
26687            this.autoSizeTabs();
26688         }
26689     },
26690
26691     /**
26692      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26693      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26694      */
26695     unhideTab : function(id){
26696         var t = this.items[id];
26697         if(t.isHidden()){
26698            t.setHidden(false);
26699            this.hiddenCount--;
26700            this.autoSizeTabs();
26701         }
26702     },
26703
26704     /**
26705      * Adds an existing {@link Roo.TabPanelItem}.
26706      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26707      */
26708     addTabItem : function(item){
26709         this.items[item.id] = item;
26710         this.items.push(item);
26711         if(this.resizeTabs){
26712            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26713            this.autoSizeTabs();
26714         }else{
26715             item.autoSize();
26716         }
26717     },
26718
26719     /**
26720      * Removes a {@link Roo.TabPanelItem}.
26721      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26722      */
26723     removeTab : function(id){
26724         var items = this.items;
26725         var tab = items[id];
26726         if(!tab) { return; }
26727         var index = items.indexOf(tab);
26728         if(this.active == tab && items.length > 1){
26729             var newTab = this.getNextAvailable(index);
26730             if(newTab) {
26731                 newTab.activate();
26732             }
26733         }
26734         this.stripEl.dom.removeChild(tab.pnode.dom);
26735         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26736             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26737         }
26738         items.splice(index, 1);
26739         delete this.items[tab.id];
26740         tab.fireEvent("close", tab);
26741         tab.purgeListeners();
26742         this.autoSizeTabs();
26743     },
26744
26745     getNextAvailable : function(start){
26746         var items = this.items;
26747         var index = start;
26748         // look for a next tab that will slide over to
26749         // replace the one being removed
26750         while(index < items.length){
26751             var item = items[++index];
26752             if(item && !item.isHidden()){
26753                 return item;
26754             }
26755         }
26756         // if one isn't found select the previous tab (on the left)
26757         index = start;
26758         while(index >= 0){
26759             var item = items[--index];
26760             if(item && !item.isHidden()){
26761                 return item;
26762             }
26763         }
26764         return null;
26765     },
26766
26767     /**
26768      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26769      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26770      */
26771     disableTab : function(id){
26772         var tab = this.items[id];
26773         if(tab && this.active != tab){
26774             tab.disable();
26775         }
26776     },
26777
26778     /**
26779      * Enables a {@link Roo.TabPanelItem} that is disabled.
26780      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26781      */
26782     enableTab : function(id){
26783         var tab = this.items[id];
26784         tab.enable();
26785     },
26786
26787     /**
26788      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26789      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26790      * @return {Roo.TabPanelItem} The TabPanelItem.
26791      */
26792     activate : function(id){
26793         var tab = this.items[id];
26794         if(!tab){
26795             return null;
26796         }
26797         if(tab == this.active || tab.disabled){
26798             return tab;
26799         }
26800         var e = {};
26801         this.fireEvent("beforetabchange", this, e, tab);
26802         if(e.cancel !== true && !tab.disabled){
26803             if(this.active){
26804                 this.active.hide();
26805             }
26806             this.active = this.items[id];
26807             this.active.show();
26808             this.fireEvent("tabchange", this, this.active);
26809         }
26810         return tab;
26811     },
26812
26813     /**
26814      * Gets the active {@link Roo.TabPanelItem}.
26815      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26816      */
26817     getActiveTab : function(){
26818         return this.active;
26819     },
26820
26821     /**
26822      * Updates the tab body element to fit the height of the container element
26823      * for overflow scrolling
26824      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26825      */
26826     syncHeight : function(targetHeight){
26827         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26828         var bm = this.bodyEl.getMargins();
26829         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26830         this.bodyEl.setHeight(newHeight);
26831         return newHeight;
26832     },
26833
26834     onResize : function(){
26835         if(this.monitorResize){
26836             this.autoSizeTabs();
26837         }
26838     },
26839
26840     /**
26841      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26842      */
26843     beginUpdate : function(){
26844         this.updating = true;
26845     },
26846
26847     /**
26848      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26849      */
26850     endUpdate : function(){
26851         this.updating = false;
26852         this.autoSizeTabs();
26853     },
26854
26855     /**
26856      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26857      */
26858     autoSizeTabs : function(){
26859         var count = this.items.length;
26860         var vcount = count - this.hiddenCount;
26861         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26862         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26863         var availWidth = Math.floor(w / vcount);
26864         var b = this.stripBody;
26865         if(b.getWidth() > w){
26866             var tabs = this.items;
26867             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26868             if(availWidth < this.minTabWidth){
26869                 /*if(!this.sleft){    // incomplete scrolling code
26870                     this.createScrollButtons();
26871                 }
26872                 this.showScroll();
26873                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26874             }
26875         }else{
26876             if(this.currentTabWidth < this.preferredTabWidth){
26877                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26878             }
26879         }
26880     },
26881
26882     /**
26883      * Returns the number of tabs in this TabPanel.
26884      * @return {Number}
26885      */
26886      getCount : function(){
26887          return this.items.length;
26888      },
26889
26890     /**
26891      * Resizes all the tabs to the passed width
26892      * @param {Number} The new width
26893      */
26894     setTabWidth : function(width){
26895         this.currentTabWidth = width;
26896         for(var i = 0, len = this.items.length; i < len; i++) {
26897                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26898         }
26899     },
26900
26901     /**
26902      * Destroys this TabPanel
26903      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26904      */
26905     destroy : function(removeEl){
26906         Roo.EventManager.removeResizeListener(this.onResize, this);
26907         for(var i = 0, len = this.items.length; i < len; i++){
26908             this.items[i].purgeListeners();
26909         }
26910         if(removeEl === true){
26911             this.el.update("");
26912             this.el.remove();
26913         }
26914     }
26915 });
26916
26917 /**
26918  * @class Roo.TabPanelItem
26919  * @extends Roo.util.Observable
26920  * Represents an individual item (tab plus body) in a TabPanel.
26921  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26922  * @param {String} id The id of this TabPanelItem
26923  * @param {String} text The text for the tab of this TabPanelItem
26924  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26925  */
26926 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26927     /**
26928      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26929      * @type Roo.TabPanel
26930      */
26931     this.tabPanel = tabPanel;
26932     /**
26933      * The id for this TabPanelItem
26934      * @type String
26935      */
26936     this.id = id;
26937     /** @private */
26938     this.disabled = false;
26939     /** @private */
26940     this.text = text;
26941     /** @private */
26942     this.loaded = false;
26943     this.closable = closable;
26944
26945     /**
26946      * The body element for this TabPanelItem.
26947      * @type Roo.Element
26948      */
26949     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26950     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26951     this.bodyEl.setStyle("display", "block");
26952     this.bodyEl.setStyle("zoom", "1");
26953     this.hideAction();
26954
26955     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26956     /** @private */
26957     this.el = Roo.get(els.el, true);
26958     this.inner = Roo.get(els.inner, true);
26959     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26960     this.pnode = Roo.get(els.el.parentNode, true);
26961     this.el.on("mousedown", this.onTabMouseDown, this);
26962     this.el.on("click", this.onTabClick, this);
26963     /** @private */
26964     if(closable){
26965         var c = Roo.get(els.close, true);
26966         c.dom.title = this.closeText;
26967         c.addClassOnOver("close-over");
26968         c.on("click", this.closeClick, this);
26969      }
26970
26971     this.addEvents({
26972          /**
26973          * @event activate
26974          * Fires when this tab becomes the active tab.
26975          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26976          * @param {Roo.TabPanelItem} this
26977          */
26978         "activate": true,
26979         /**
26980          * @event beforeclose
26981          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26982          * @param {Roo.TabPanelItem} this
26983          * @param {Object} e Set cancel to true on this object to cancel the close.
26984          */
26985         "beforeclose": true,
26986         /**
26987          * @event close
26988          * Fires when this tab is closed.
26989          * @param {Roo.TabPanelItem} this
26990          */
26991          "close": true,
26992         /**
26993          * @event deactivate
26994          * Fires when this tab is no longer the active tab.
26995          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26996          * @param {Roo.TabPanelItem} this
26997          */
26998          "deactivate" : true
26999     });
27000     this.hidden = false;
27001
27002     Roo.TabPanelItem.superclass.constructor.call(this);
27003 };
27004
27005 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27006     purgeListeners : function(){
27007        Roo.util.Observable.prototype.purgeListeners.call(this);
27008        this.el.removeAllListeners();
27009     },
27010     /**
27011      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27012      */
27013     show : function(){
27014         this.pnode.addClass("on");
27015         this.showAction();
27016         if(Roo.isOpera){
27017             this.tabPanel.stripWrap.repaint();
27018         }
27019         this.fireEvent("activate", this.tabPanel, this);
27020     },
27021
27022     /**
27023      * Returns true if this tab is the active tab.
27024      * @return {Boolean}
27025      */
27026     isActive : function(){
27027         return this.tabPanel.getActiveTab() == this;
27028     },
27029
27030     /**
27031      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27032      */
27033     hide : function(){
27034         this.pnode.removeClass("on");
27035         this.hideAction();
27036         this.fireEvent("deactivate", this.tabPanel, this);
27037     },
27038
27039     hideAction : function(){
27040         this.bodyEl.hide();
27041         this.bodyEl.setStyle("position", "absolute");
27042         this.bodyEl.setLeft("-20000px");
27043         this.bodyEl.setTop("-20000px");
27044     },
27045
27046     showAction : function(){
27047         this.bodyEl.setStyle("position", "relative");
27048         this.bodyEl.setTop("");
27049         this.bodyEl.setLeft("");
27050         this.bodyEl.show();
27051     },
27052
27053     /**
27054      * Set the tooltip for the tab.
27055      * @param {String} tooltip The tab's tooltip
27056      */
27057     setTooltip : function(text){
27058         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27059             this.textEl.dom.qtip = text;
27060             this.textEl.dom.removeAttribute('title');
27061         }else{
27062             this.textEl.dom.title = text;
27063         }
27064     },
27065
27066     onTabClick : function(e){
27067         e.preventDefault();
27068         this.tabPanel.activate(this.id);
27069     },
27070
27071     onTabMouseDown : function(e){
27072         e.preventDefault();
27073         this.tabPanel.activate(this.id);
27074     },
27075
27076     getWidth : function(){
27077         return this.inner.getWidth();
27078     },
27079
27080     setWidth : function(width){
27081         var iwidth = width - this.pnode.getPadding("lr");
27082         this.inner.setWidth(iwidth);
27083         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27084         this.pnode.setWidth(width);
27085     },
27086
27087     /**
27088      * Show or hide the tab
27089      * @param {Boolean} hidden True to hide or false to show.
27090      */
27091     setHidden : function(hidden){
27092         this.hidden = hidden;
27093         this.pnode.setStyle("display", hidden ? "none" : "");
27094     },
27095
27096     /**
27097      * Returns true if this tab is "hidden"
27098      * @return {Boolean}
27099      */
27100     isHidden : function(){
27101         return this.hidden;
27102     },
27103
27104     /**
27105      * Returns the text for this tab
27106      * @return {String}
27107      */
27108     getText : function(){
27109         return this.text;
27110     },
27111
27112     autoSize : function(){
27113         //this.el.beginMeasure();
27114         this.textEl.setWidth(1);
27115         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27116         //this.el.endMeasure();
27117     },
27118
27119     /**
27120      * Sets the text for the tab (Note: this also sets the tooltip text)
27121      * @param {String} text The tab's text and tooltip
27122      */
27123     setText : function(text){
27124         this.text = text;
27125         this.textEl.update(text);
27126         this.setTooltip(text);
27127         if(!this.tabPanel.resizeTabs){
27128             this.autoSize();
27129         }
27130     },
27131     /**
27132      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27133      */
27134     activate : function(){
27135         this.tabPanel.activate(this.id);
27136     },
27137
27138     /**
27139      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27140      */
27141     disable : function(){
27142         if(this.tabPanel.active != this){
27143             this.disabled = true;
27144             this.pnode.addClass("disabled");
27145         }
27146     },
27147
27148     /**
27149      * Enables this TabPanelItem if it was previously disabled.
27150      */
27151     enable : function(){
27152         this.disabled = false;
27153         this.pnode.removeClass("disabled");
27154     },
27155
27156     /**
27157      * Sets the content for this TabPanelItem.
27158      * @param {String} content The content
27159      * @param {Boolean} loadScripts true to look for and load scripts
27160      */
27161     setContent : function(content, loadScripts){
27162         this.bodyEl.update(content, loadScripts);
27163     },
27164
27165     /**
27166      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27167      * @return {Roo.UpdateManager} The UpdateManager
27168      */
27169     getUpdateManager : function(){
27170         return this.bodyEl.getUpdateManager();
27171     },
27172
27173     /**
27174      * Set a URL to be used to load the content for this TabPanelItem.
27175      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27176      * @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)
27177      * @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)
27178      * @return {Roo.UpdateManager} The UpdateManager
27179      */
27180     setUrl : function(url, params, loadOnce){
27181         if(this.refreshDelegate){
27182             this.un('activate', this.refreshDelegate);
27183         }
27184         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27185         this.on("activate", this.refreshDelegate);
27186         return this.bodyEl.getUpdateManager();
27187     },
27188
27189     /** @private */
27190     _handleRefresh : function(url, params, loadOnce){
27191         if(!loadOnce || !this.loaded){
27192             var updater = this.bodyEl.getUpdateManager();
27193             updater.update(url, params, this._setLoaded.createDelegate(this));
27194         }
27195     },
27196
27197     /**
27198      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27199      *   Will fail silently if the setUrl method has not been called.
27200      *   This does not activate the panel, just updates its content.
27201      */
27202     refresh : function(){
27203         if(this.refreshDelegate){
27204            this.loaded = false;
27205            this.refreshDelegate();
27206         }
27207     },
27208
27209     /** @private */
27210     _setLoaded : function(){
27211         this.loaded = true;
27212     },
27213
27214     /** @private */
27215     closeClick : function(e){
27216         var o = {};
27217         e.stopEvent();
27218         this.fireEvent("beforeclose", this, o);
27219         if(o.cancel !== true){
27220             this.tabPanel.removeTab(this.id);
27221         }
27222     },
27223     /**
27224      * The text displayed in the tooltip for the close icon.
27225      * @type String
27226      */
27227     closeText : "Close this tab"
27228 });
27229
27230 /** @private */
27231 Roo.TabPanel.prototype.createStrip = function(container){
27232     var strip = document.createElement("div");
27233     strip.className = "x-tabs-wrap";
27234     container.appendChild(strip);
27235     return strip;
27236 };
27237 /** @private */
27238 Roo.TabPanel.prototype.createStripList = function(strip){
27239     // div wrapper for retard IE
27240     // returns the "tr" element.
27241     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27242         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27243         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27244     return strip.firstChild.firstChild.firstChild.firstChild;
27245 };
27246 /** @private */
27247 Roo.TabPanel.prototype.createBody = function(container){
27248     var body = document.createElement("div");
27249     Roo.id(body, "tab-body");
27250     Roo.fly(body).addClass("x-tabs-body");
27251     container.appendChild(body);
27252     return body;
27253 };
27254 /** @private */
27255 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27256     var body = Roo.getDom(id);
27257     if(!body){
27258         body = document.createElement("div");
27259         body.id = id;
27260     }
27261     Roo.fly(body).addClass("x-tabs-item-body");
27262     bodyEl.insertBefore(body, bodyEl.firstChild);
27263     return body;
27264 };
27265 /** @private */
27266 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27267     var td = document.createElement("td");
27268     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27269     //stripEl.appendChild(td);
27270     if(closable){
27271         td.className = "x-tabs-closable";
27272         if(!this.closeTpl){
27273             this.closeTpl = new Roo.Template(
27274                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27275                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27276                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27277             );
27278         }
27279         var el = this.closeTpl.overwrite(td, {"text": text});
27280         var close = el.getElementsByTagName("div")[0];
27281         var inner = el.getElementsByTagName("em")[0];
27282         return {"el": el, "close": close, "inner": inner};
27283     } else {
27284         if(!this.tabTpl){
27285             this.tabTpl = new Roo.Template(
27286                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27287                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27288             );
27289         }
27290         var el = this.tabTpl.overwrite(td, {"text": text});
27291         var inner = el.getElementsByTagName("em")[0];
27292         return {"el": el, "inner": inner};
27293     }
27294 };/*
27295  * Based on:
27296  * Ext JS Library 1.1.1
27297  * Copyright(c) 2006-2007, Ext JS, LLC.
27298  *
27299  * Originally Released Under LGPL - original licence link has changed is not relivant.
27300  *
27301  * Fork - LGPL
27302  * <script type="text/javascript">
27303  */
27304
27305 /**
27306  * @class Roo.Button
27307  * @extends Roo.util.Observable
27308  * Simple Button class
27309  * @cfg {String} text The button text
27310  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27311  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27312  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27313  * @cfg {Object} scope The scope of the handler
27314  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27315  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27316  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27317  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27318  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27319  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27320    applies if enableToggle = true)
27321  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27322  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27323   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27324  * @constructor
27325  * Create a new button
27326  * @param {Object} config The config object
27327  */
27328 Roo.Button = function(renderTo, config)
27329 {
27330     if (!config) {
27331         config = renderTo;
27332         renderTo = config.renderTo || false;
27333     }
27334     
27335     Roo.apply(this, config);
27336     this.addEvents({
27337         /**
27338              * @event click
27339              * Fires when this button is clicked
27340              * @param {Button} this
27341              * @param {EventObject} e The click event
27342              */
27343             "click" : true,
27344         /**
27345              * @event toggle
27346              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27347              * @param {Button} this
27348              * @param {Boolean} pressed
27349              */
27350             "toggle" : true,
27351         /**
27352              * @event mouseover
27353              * Fires when the mouse hovers over the button
27354              * @param {Button} this
27355              * @param {Event} e The event object
27356              */
27357         'mouseover' : true,
27358         /**
27359              * @event mouseout
27360              * Fires when the mouse exits the button
27361              * @param {Button} this
27362              * @param {Event} e The event object
27363              */
27364         'mouseout': true,
27365          /**
27366              * @event render
27367              * Fires when the button is rendered
27368              * @param {Button} this
27369              */
27370         'render': true
27371     });
27372     if(this.menu){
27373         this.menu = Roo.menu.MenuMgr.get(this.menu);
27374     }
27375     // register listeners first!!  - so render can be captured..
27376     Roo.util.Observable.call(this);
27377     if(renderTo){
27378         this.render(renderTo);
27379     }
27380     
27381   
27382 };
27383
27384 Roo.extend(Roo.Button, Roo.util.Observable, {
27385     /**
27386      * 
27387      */
27388     
27389     /**
27390      * Read-only. True if this button is hidden
27391      * @type Boolean
27392      */
27393     hidden : false,
27394     /**
27395      * Read-only. True if this button is disabled
27396      * @type Boolean
27397      */
27398     disabled : false,
27399     /**
27400      * Read-only. True if this button is pressed (only if enableToggle = true)
27401      * @type Boolean
27402      */
27403     pressed : false,
27404
27405     /**
27406      * @cfg {Number} tabIndex 
27407      * The DOM tabIndex for this button (defaults to undefined)
27408      */
27409     tabIndex : undefined,
27410
27411     /**
27412      * @cfg {Boolean} enableToggle
27413      * True to enable pressed/not pressed toggling (defaults to false)
27414      */
27415     enableToggle: false,
27416     /**
27417      * @cfg {Mixed} menu
27418      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27419      */
27420     menu : undefined,
27421     /**
27422      * @cfg {String} menuAlign
27423      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27424      */
27425     menuAlign : "tl-bl?",
27426
27427     /**
27428      * @cfg {String} iconCls
27429      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27430      */
27431     iconCls : undefined,
27432     /**
27433      * @cfg {String} type
27434      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27435      */
27436     type : 'button',
27437
27438     // private
27439     menuClassTarget: 'tr',
27440
27441     /**
27442      * @cfg {String} clickEvent
27443      * The type of event to map to the button's event handler (defaults to 'click')
27444      */
27445     clickEvent : 'click',
27446
27447     /**
27448      * @cfg {Boolean} handleMouseEvents
27449      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27450      */
27451     handleMouseEvents : true,
27452
27453     /**
27454      * @cfg {String} tooltipType
27455      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27456      */
27457     tooltipType : 'qtip',
27458
27459     /**
27460      * @cfg {String} cls
27461      * A CSS class to apply to the button's main element.
27462      */
27463     
27464     /**
27465      * @cfg {Roo.Template} template (Optional)
27466      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27467      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27468      * require code modifications if required elements (e.g. a button) aren't present.
27469      */
27470
27471     // private
27472     render : function(renderTo){
27473         var btn;
27474         if(this.hideParent){
27475             this.parentEl = Roo.get(renderTo);
27476         }
27477         if(!this.dhconfig){
27478             if(!this.template){
27479                 if(!Roo.Button.buttonTemplate){
27480                     // hideous table template
27481                     Roo.Button.buttonTemplate = new Roo.Template(
27482                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27483                         '<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>',
27484                         "</tr></tbody></table>");
27485                 }
27486                 this.template = Roo.Button.buttonTemplate;
27487             }
27488             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27489             var btnEl = btn.child("button:first");
27490             btnEl.on('focus', this.onFocus, this);
27491             btnEl.on('blur', this.onBlur, this);
27492             if(this.cls){
27493                 btn.addClass(this.cls);
27494             }
27495             if(this.icon){
27496                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27497             }
27498             if(this.iconCls){
27499                 btnEl.addClass(this.iconCls);
27500                 if(!this.cls){
27501                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27502                 }
27503             }
27504             if(this.tabIndex !== undefined){
27505                 btnEl.dom.tabIndex = this.tabIndex;
27506             }
27507             if(this.tooltip){
27508                 if(typeof this.tooltip == 'object'){
27509                     Roo.QuickTips.tips(Roo.apply({
27510                           target: btnEl.id
27511                     }, this.tooltip));
27512                 } else {
27513                     btnEl.dom[this.tooltipType] = this.tooltip;
27514                 }
27515             }
27516         }else{
27517             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27518         }
27519         this.el = btn;
27520         if(this.id){
27521             this.el.dom.id = this.el.id = this.id;
27522         }
27523         if(this.menu){
27524             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27525             this.menu.on("show", this.onMenuShow, this);
27526             this.menu.on("hide", this.onMenuHide, this);
27527         }
27528         btn.addClass("x-btn");
27529         if(Roo.isIE && !Roo.isIE7){
27530             this.autoWidth.defer(1, this);
27531         }else{
27532             this.autoWidth();
27533         }
27534         if(this.handleMouseEvents){
27535             btn.on("mouseover", this.onMouseOver, this);
27536             btn.on("mouseout", this.onMouseOut, this);
27537             btn.on("mousedown", this.onMouseDown, this);
27538         }
27539         btn.on(this.clickEvent, this.onClick, this);
27540         //btn.on("mouseup", this.onMouseUp, this);
27541         if(this.hidden){
27542             this.hide();
27543         }
27544         if(this.disabled){
27545             this.disable();
27546         }
27547         Roo.ButtonToggleMgr.register(this);
27548         if(this.pressed){
27549             this.el.addClass("x-btn-pressed");
27550         }
27551         if(this.repeat){
27552             var repeater = new Roo.util.ClickRepeater(btn,
27553                 typeof this.repeat == "object" ? this.repeat : {}
27554             );
27555             repeater.on("click", this.onClick,  this);
27556         }
27557         
27558         this.fireEvent('render', this);
27559         
27560     },
27561     /**
27562      * Returns the button's underlying element
27563      * @return {Roo.Element} The element
27564      */
27565     getEl : function(){
27566         return this.el;  
27567     },
27568     
27569     /**
27570      * Destroys this Button and removes any listeners.
27571      */
27572     destroy : function(){
27573         Roo.ButtonToggleMgr.unregister(this);
27574         this.el.removeAllListeners();
27575         this.purgeListeners();
27576         this.el.remove();
27577     },
27578
27579     // private
27580     autoWidth : function(){
27581         if(this.el){
27582             this.el.setWidth("auto");
27583             if(Roo.isIE7 && Roo.isStrict){
27584                 var ib = this.el.child('button');
27585                 if(ib && ib.getWidth() > 20){
27586                     ib.clip();
27587                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27588                 }
27589             }
27590             if(this.minWidth){
27591                 if(this.hidden){
27592                     this.el.beginMeasure();
27593                 }
27594                 if(this.el.getWidth() < this.minWidth){
27595                     this.el.setWidth(this.minWidth);
27596                 }
27597                 if(this.hidden){
27598                     this.el.endMeasure();
27599                 }
27600             }
27601         }
27602     },
27603
27604     /**
27605      * Assigns this button's click handler
27606      * @param {Function} handler The function to call when the button is clicked
27607      * @param {Object} scope (optional) Scope for the function passed in
27608      */
27609     setHandler : function(handler, scope){
27610         this.handler = handler;
27611         this.scope = scope;  
27612     },
27613     
27614     /**
27615      * Sets this button's text
27616      * @param {String} text The button text
27617      */
27618     setText : function(text){
27619         this.text = text;
27620         if(this.el){
27621             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27622         }
27623         this.autoWidth();
27624     },
27625     
27626     /**
27627      * Gets the text for this button
27628      * @return {String} The button text
27629      */
27630     getText : function(){
27631         return this.text;  
27632     },
27633     
27634     /**
27635      * Show this button
27636      */
27637     show: function(){
27638         this.hidden = false;
27639         if(this.el){
27640             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27641         }
27642     },
27643     
27644     /**
27645      * Hide this button
27646      */
27647     hide: function(){
27648         this.hidden = true;
27649         if(this.el){
27650             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27651         }
27652     },
27653     
27654     /**
27655      * Convenience function for boolean show/hide
27656      * @param {Boolean} visible True to show, false to hide
27657      */
27658     setVisible: function(visible){
27659         if(visible) {
27660             this.show();
27661         }else{
27662             this.hide();
27663         }
27664     },
27665     
27666     /**
27667      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27668      * @param {Boolean} state (optional) Force a particular state
27669      */
27670     toggle : function(state){
27671         state = state === undefined ? !this.pressed : state;
27672         if(state != this.pressed){
27673             if(state){
27674                 this.el.addClass("x-btn-pressed");
27675                 this.pressed = true;
27676                 this.fireEvent("toggle", this, true);
27677             }else{
27678                 this.el.removeClass("x-btn-pressed");
27679                 this.pressed = false;
27680                 this.fireEvent("toggle", this, false);
27681             }
27682             if(this.toggleHandler){
27683                 this.toggleHandler.call(this.scope || this, this, state);
27684             }
27685         }
27686     },
27687     
27688     /**
27689      * Focus the button
27690      */
27691     focus : function(){
27692         this.el.child('button:first').focus();
27693     },
27694     
27695     /**
27696      * Disable this button
27697      */
27698     disable : function(){
27699         if(this.el){
27700             this.el.addClass("x-btn-disabled");
27701         }
27702         this.disabled = true;
27703     },
27704     
27705     /**
27706      * Enable this button
27707      */
27708     enable : function(){
27709         if(this.el){
27710             this.el.removeClass("x-btn-disabled");
27711         }
27712         this.disabled = false;
27713     },
27714
27715     /**
27716      * Convenience function for boolean enable/disable
27717      * @param {Boolean} enabled True to enable, false to disable
27718      */
27719     setDisabled : function(v){
27720         this[v !== true ? "enable" : "disable"]();
27721     },
27722
27723     // private
27724     onClick : function(e){
27725         if(e){
27726             e.preventDefault();
27727         }
27728         if(e.button != 0){
27729             return;
27730         }
27731         if(!this.disabled){
27732             if(this.enableToggle){
27733                 this.toggle();
27734             }
27735             if(this.menu && !this.menu.isVisible()){
27736                 this.menu.show(this.el, this.menuAlign);
27737             }
27738             this.fireEvent("click", this, e);
27739             if(this.handler){
27740                 this.el.removeClass("x-btn-over");
27741                 this.handler.call(this.scope || this, this, e);
27742             }
27743         }
27744     },
27745     // private
27746     onMouseOver : function(e){
27747         if(!this.disabled){
27748             this.el.addClass("x-btn-over");
27749             this.fireEvent('mouseover', this, e);
27750         }
27751     },
27752     // private
27753     onMouseOut : function(e){
27754         if(!e.within(this.el,  true)){
27755             this.el.removeClass("x-btn-over");
27756             this.fireEvent('mouseout', this, e);
27757         }
27758     },
27759     // private
27760     onFocus : function(e){
27761         if(!this.disabled){
27762             this.el.addClass("x-btn-focus");
27763         }
27764     },
27765     // private
27766     onBlur : function(e){
27767         this.el.removeClass("x-btn-focus");
27768     },
27769     // private
27770     onMouseDown : function(e){
27771         if(!this.disabled && e.button == 0){
27772             this.el.addClass("x-btn-click");
27773             Roo.get(document).on('mouseup', this.onMouseUp, this);
27774         }
27775     },
27776     // private
27777     onMouseUp : function(e){
27778         if(e.button == 0){
27779             this.el.removeClass("x-btn-click");
27780             Roo.get(document).un('mouseup', this.onMouseUp, this);
27781         }
27782     },
27783     // private
27784     onMenuShow : function(e){
27785         this.el.addClass("x-btn-menu-active");
27786     },
27787     // private
27788     onMenuHide : function(e){
27789         this.el.removeClass("x-btn-menu-active");
27790     }   
27791 });
27792
27793 // Private utility class used by Button
27794 Roo.ButtonToggleMgr = function(){
27795    var groups = {};
27796    
27797    function toggleGroup(btn, state){
27798        if(state){
27799            var g = groups[btn.toggleGroup];
27800            for(var i = 0, l = g.length; i < l; i++){
27801                if(g[i] != btn){
27802                    g[i].toggle(false);
27803                }
27804            }
27805        }
27806    }
27807    
27808    return {
27809        register : function(btn){
27810            if(!btn.toggleGroup){
27811                return;
27812            }
27813            var g = groups[btn.toggleGroup];
27814            if(!g){
27815                g = groups[btn.toggleGroup] = [];
27816            }
27817            g.push(btn);
27818            btn.on("toggle", toggleGroup);
27819        },
27820        
27821        unregister : function(btn){
27822            if(!btn.toggleGroup){
27823                return;
27824            }
27825            var g = groups[btn.toggleGroup];
27826            if(g){
27827                g.remove(btn);
27828                btn.un("toggle", toggleGroup);
27829            }
27830        }
27831    };
27832 }();/*
27833  * Based on:
27834  * Ext JS Library 1.1.1
27835  * Copyright(c) 2006-2007, Ext JS, LLC.
27836  *
27837  * Originally Released Under LGPL - original licence link has changed is not relivant.
27838  *
27839  * Fork - LGPL
27840  * <script type="text/javascript">
27841  */
27842  
27843 /**
27844  * @class Roo.SplitButton
27845  * @extends Roo.Button
27846  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27847  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27848  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27849  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27850  * @cfg {String} arrowTooltip The title attribute of the arrow
27851  * @constructor
27852  * Create a new menu button
27853  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27854  * @param {Object} config The config object
27855  */
27856 Roo.SplitButton = function(renderTo, config){
27857     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27858     /**
27859      * @event arrowclick
27860      * Fires when this button's arrow is clicked
27861      * @param {SplitButton} this
27862      * @param {EventObject} e The click event
27863      */
27864     this.addEvents({"arrowclick":true});
27865 };
27866
27867 Roo.extend(Roo.SplitButton, Roo.Button, {
27868     render : function(renderTo){
27869         // this is one sweet looking template!
27870         var tpl = new Roo.Template(
27871             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27872             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27873             '<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>',
27874             "</tbody></table></td><td>",
27875             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27876             '<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>',
27877             "</tbody></table></td></tr></table>"
27878         );
27879         var btn = tpl.append(renderTo, [this.text, this.type], true);
27880         var btnEl = btn.child("button");
27881         if(this.cls){
27882             btn.addClass(this.cls);
27883         }
27884         if(this.icon){
27885             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27886         }
27887         if(this.iconCls){
27888             btnEl.addClass(this.iconCls);
27889             if(!this.cls){
27890                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27891             }
27892         }
27893         this.el = btn;
27894         if(this.handleMouseEvents){
27895             btn.on("mouseover", this.onMouseOver, this);
27896             btn.on("mouseout", this.onMouseOut, this);
27897             btn.on("mousedown", this.onMouseDown, this);
27898             btn.on("mouseup", this.onMouseUp, this);
27899         }
27900         btn.on(this.clickEvent, this.onClick, this);
27901         if(this.tooltip){
27902             if(typeof this.tooltip == 'object'){
27903                 Roo.QuickTips.tips(Roo.apply({
27904                       target: btnEl.id
27905                 }, this.tooltip));
27906             } else {
27907                 btnEl.dom[this.tooltipType] = this.tooltip;
27908             }
27909         }
27910         if(this.arrowTooltip){
27911             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27912         }
27913         if(this.hidden){
27914             this.hide();
27915         }
27916         if(this.disabled){
27917             this.disable();
27918         }
27919         if(this.pressed){
27920             this.el.addClass("x-btn-pressed");
27921         }
27922         if(Roo.isIE && !Roo.isIE7){
27923             this.autoWidth.defer(1, this);
27924         }else{
27925             this.autoWidth();
27926         }
27927         if(this.menu){
27928             this.menu.on("show", this.onMenuShow, this);
27929             this.menu.on("hide", this.onMenuHide, this);
27930         }
27931         this.fireEvent('render', this);
27932     },
27933
27934     // private
27935     autoWidth : function(){
27936         if(this.el){
27937             var tbl = this.el.child("table:first");
27938             var tbl2 = this.el.child("table:last");
27939             this.el.setWidth("auto");
27940             tbl.setWidth("auto");
27941             if(Roo.isIE7 && Roo.isStrict){
27942                 var ib = this.el.child('button:first');
27943                 if(ib && ib.getWidth() > 20){
27944                     ib.clip();
27945                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27946                 }
27947             }
27948             if(this.minWidth){
27949                 if(this.hidden){
27950                     this.el.beginMeasure();
27951                 }
27952                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27953                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27954                 }
27955                 if(this.hidden){
27956                     this.el.endMeasure();
27957                 }
27958             }
27959             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27960         } 
27961     },
27962     /**
27963      * Sets this button's click handler
27964      * @param {Function} handler The function to call when the button is clicked
27965      * @param {Object} scope (optional) Scope for the function passed above
27966      */
27967     setHandler : function(handler, scope){
27968         this.handler = handler;
27969         this.scope = scope;  
27970     },
27971     
27972     /**
27973      * Sets this button's arrow click handler
27974      * @param {Function} handler The function to call when the arrow is clicked
27975      * @param {Object} scope (optional) Scope for the function passed above
27976      */
27977     setArrowHandler : function(handler, scope){
27978         this.arrowHandler = handler;
27979         this.scope = scope;  
27980     },
27981     
27982     /**
27983      * Focus the button
27984      */
27985     focus : function(){
27986         if(this.el){
27987             this.el.child("button:first").focus();
27988         }
27989     },
27990
27991     // private
27992     onClick : function(e){
27993         e.preventDefault();
27994         if(!this.disabled){
27995             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27996                 if(this.menu && !this.menu.isVisible()){
27997                     this.menu.show(this.el, this.menuAlign);
27998                 }
27999                 this.fireEvent("arrowclick", this, e);
28000                 if(this.arrowHandler){
28001                     this.arrowHandler.call(this.scope || this, this, e);
28002                 }
28003             }else{
28004                 this.fireEvent("click", this, e);
28005                 if(this.handler){
28006                     this.handler.call(this.scope || this, this, e);
28007                 }
28008             }
28009         }
28010     },
28011     // private
28012     onMouseDown : function(e){
28013         if(!this.disabled){
28014             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28015         }
28016     },
28017     // private
28018     onMouseUp : function(e){
28019         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28020     }   
28021 });
28022
28023
28024 // backwards compat
28025 Roo.MenuButton = Roo.SplitButton;/*
28026  * Based on:
28027  * Ext JS Library 1.1.1
28028  * Copyright(c) 2006-2007, Ext JS, LLC.
28029  *
28030  * Originally Released Under LGPL - original licence link has changed is not relivant.
28031  *
28032  * Fork - LGPL
28033  * <script type="text/javascript">
28034  */
28035
28036 /**
28037  * @class Roo.Toolbar
28038  * Basic Toolbar class.
28039  * @constructor
28040  * Creates a new Toolbar
28041  * @param {Object} container The config object
28042  */ 
28043 Roo.Toolbar = function(container, buttons, config)
28044 {
28045     /// old consturctor format still supported..
28046     if(container instanceof Array){ // omit the container for later rendering
28047         buttons = container;
28048         config = buttons;
28049         container = null;
28050     }
28051     if (typeof(container) == 'object' && container.xtype) {
28052         config = container;
28053         container = config.container;
28054         buttons = config.buttons || []; // not really - use items!!
28055     }
28056     var xitems = [];
28057     if (config && config.items) {
28058         xitems = config.items;
28059         delete config.items;
28060     }
28061     Roo.apply(this, config);
28062     this.buttons = buttons;
28063     
28064     if(container){
28065         this.render(container);
28066     }
28067     this.xitems = xitems;
28068     Roo.each(xitems, function(b) {
28069         this.add(b);
28070     }, this);
28071     
28072 };
28073
28074 Roo.Toolbar.prototype = {
28075     /**
28076      * @cfg {Array} items
28077      * array of button configs or elements to add (will be converted to a MixedCollection)
28078      */
28079     
28080     /**
28081      * @cfg {String/HTMLElement/Element} container
28082      * The id or element that will contain the toolbar
28083      */
28084     // private
28085     render : function(ct){
28086         this.el = Roo.get(ct);
28087         if(this.cls){
28088             this.el.addClass(this.cls);
28089         }
28090         // using a table allows for vertical alignment
28091         // 100% width is needed by Safari...
28092         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28093         this.tr = this.el.child("tr", true);
28094         var autoId = 0;
28095         this.items = new Roo.util.MixedCollection(false, function(o){
28096             return o.id || ("item" + (++autoId));
28097         });
28098         if(this.buttons){
28099             this.add.apply(this, this.buttons);
28100             delete this.buttons;
28101         }
28102     },
28103
28104     /**
28105      * Adds element(s) to the toolbar -- this function takes a variable number of 
28106      * arguments of mixed type and adds them to the toolbar.
28107      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28108      * <ul>
28109      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28110      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28111      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28112      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28113      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28114      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28115      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28116      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28117      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28118      * </ul>
28119      * @param {Mixed} arg2
28120      * @param {Mixed} etc.
28121      */
28122     add : function(){
28123         var a = arguments, l = a.length;
28124         for(var i = 0; i < l; i++){
28125             this._add(a[i]);
28126         }
28127     },
28128     // private..
28129     _add : function(el) {
28130         
28131         if (el.xtype) {
28132             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28133         }
28134         
28135         if (el.applyTo){ // some kind of form field
28136             return this.addField(el);
28137         } 
28138         if (el.render){ // some kind of Toolbar.Item
28139             return this.addItem(el);
28140         }
28141         if (typeof el == "string"){ // string
28142             if(el == "separator" || el == "-"){
28143                 return this.addSeparator();
28144             }
28145             if (el == " "){
28146                 return this.addSpacer();
28147             }
28148             if(el == "->"){
28149                 return this.addFill();
28150             }
28151             return this.addText(el);
28152             
28153         }
28154         if(el.tagName){ // element
28155             return this.addElement(el);
28156         }
28157         if(typeof el == "object"){ // must be button config?
28158             return this.addButton(el);
28159         }
28160         // and now what?!?!
28161         return false;
28162         
28163     },
28164     
28165     /**
28166      * Add an Xtype element
28167      * @param {Object} xtype Xtype Object
28168      * @return {Object} created Object
28169      */
28170     addxtype : function(e){
28171         return this.add(e);  
28172     },
28173     
28174     /**
28175      * Returns the Element for this toolbar.
28176      * @return {Roo.Element}
28177      */
28178     getEl : function(){
28179         return this.el;  
28180     },
28181     
28182     /**
28183      * Adds a separator
28184      * @return {Roo.Toolbar.Item} The separator item
28185      */
28186     addSeparator : function(){
28187         return this.addItem(new Roo.Toolbar.Separator());
28188     },
28189
28190     /**
28191      * Adds a spacer element
28192      * @return {Roo.Toolbar.Spacer} The spacer item
28193      */
28194     addSpacer : function(){
28195         return this.addItem(new Roo.Toolbar.Spacer());
28196     },
28197
28198     /**
28199      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28200      * @return {Roo.Toolbar.Fill} The fill item
28201      */
28202     addFill : function(){
28203         return this.addItem(new Roo.Toolbar.Fill());
28204     },
28205
28206     /**
28207      * Adds any standard HTML element to the toolbar
28208      * @param {String/HTMLElement/Element} el The element or id of the element to add
28209      * @return {Roo.Toolbar.Item} The element's item
28210      */
28211     addElement : function(el){
28212         return this.addItem(new Roo.Toolbar.Item(el));
28213     },
28214     /**
28215      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28216      * @type Roo.util.MixedCollection  
28217      */
28218     items : false,
28219      
28220     /**
28221      * Adds any Toolbar.Item or subclass
28222      * @param {Roo.Toolbar.Item} item
28223      * @return {Roo.Toolbar.Item} The item
28224      */
28225     addItem : function(item){
28226         var td = this.nextBlock();
28227         item.render(td);
28228         this.items.add(item);
28229         return item;
28230     },
28231     
28232     /**
28233      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28234      * @param {Object/Array} config A button config or array of configs
28235      * @return {Roo.Toolbar.Button/Array}
28236      */
28237     addButton : function(config){
28238         if(config instanceof Array){
28239             var buttons = [];
28240             for(var i = 0, len = config.length; i < len; i++) {
28241                 buttons.push(this.addButton(config[i]));
28242             }
28243             return buttons;
28244         }
28245         var b = config;
28246         if(!(config instanceof Roo.Toolbar.Button)){
28247             b = config.split ?
28248                 new Roo.Toolbar.SplitButton(config) :
28249                 new Roo.Toolbar.Button(config);
28250         }
28251         var td = this.nextBlock();
28252         b.render(td);
28253         this.items.add(b);
28254         return b;
28255     },
28256     
28257     /**
28258      * Adds text to the toolbar
28259      * @param {String} text The text to add
28260      * @return {Roo.Toolbar.Item} The element's item
28261      */
28262     addText : function(text){
28263         return this.addItem(new Roo.Toolbar.TextItem(text));
28264     },
28265     
28266     /**
28267      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28268      * @param {Number} index The index where the item is to be inserted
28269      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28270      * @return {Roo.Toolbar.Button/Item}
28271      */
28272     insertButton : function(index, item){
28273         if(item instanceof Array){
28274             var buttons = [];
28275             for(var i = 0, len = item.length; i < len; i++) {
28276                buttons.push(this.insertButton(index + i, item[i]));
28277             }
28278             return buttons;
28279         }
28280         if (!(item instanceof Roo.Toolbar.Button)){
28281            item = new Roo.Toolbar.Button(item);
28282         }
28283         var td = document.createElement("td");
28284         this.tr.insertBefore(td, this.tr.childNodes[index]);
28285         item.render(td);
28286         this.items.insert(index, item);
28287         return item;
28288     },
28289     
28290     /**
28291      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28292      * @param {Object} config
28293      * @return {Roo.Toolbar.Item} The element's item
28294      */
28295     addDom : function(config, returnEl){
28296         var td = this.nextBlock();
28297         Roo.DomHelper.overwrite(td, config);
28298         var ti = new Roo.Toolbar.Item(td.firstChild);
28299         ti.render(td);
28300         this.items.add(ti);
28301         return ti;
28302     },
28303
28304     /**
28305      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28306      * @type Roo.util.MixedCollection  
28307      */
28308     fields : false,
28309     
28310     /**
28311      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28312      * Note: the field should not have been rendered yet. For a field that has already been
28313      * rendered, use {@link #addElement}.
28314      * @param {Roo.form.Field} field
28315      * @return {Roo.ToolbarItem}
28316      */
28317      
28318       
28319     addField : function(field) {
28320         if (!this.fields) {
28321             var autoId = 0;
28322             this.fields = new Roo.util.MixedCollection(false, function(o){
28323                 return o.id || ("item" + (++autoId));
28324             });
28325
28326         }
28327         
28328         var td = this.nextBlock();
28329         field.render(td);
28330         var ti = new Roo.Toolbar.Item(td.firstChild);
28331         ti.render(td);
28332         this.items.add(ti);
28333         this.fields.add(field);
28334         return ti;
28335     },
28336     /**
28337      * Hide the toolbar
28338      * @method hide
28339      */
28340      
28341       
28342     hide : function()
28343     {
28344         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28345         this.el.child('div').hide();
28346     },
28347     /**
28348      * Show the toolbar
28349      * @method show
28350      */
28351     show : function()
28352     {
28353         this.el.child('div').show();
28354     },
28355       
28356     // private
28357     nextBlock : function(){
28358         var td = document.createElement("td");
28359         this.tr.appendChild(td);
28360         return td;
28361     },
28362
28363     // private
28364     destroy : function(){
28365         if(this.items){ // rendered?
28366             Roo.destroy.apply(Roo, this.items.items);
28367         }
28368         if(this.fields){ // rendered?
28369             Roo.destroy.apply(Roo, this.fields.items);
28370         }
28371         Roo.Element.uncache(this.el, this.tr);
28372     }
28373 };
28374
28375 /**
28376  * @class Roo.Toolbar.Item
28377  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28378  * @constructor
28379  * Creates a new Item
28380  * @param {HTMLElement} el 
28381  */
28382 Roo.Toolbar.Item = function(el){
28383     this.el = Roo.getDom(el);
28384     this.id = Roo.id(this.el);
28385     this.hidden = false;
28386 };
28387
28388 Roo.Toolbar.Item.prototype = {
28389     
28390     /**
28391      * Get this item's HTML Element
28392      * @return {HTMLElement}
28393      */
28394     getEl : function(){
28395        return this.el;  
28396     },
28397
28398     // private
28399     render : function(td){
28400         this.td = td;
28401         td.appendChild(this.el);
28402     },
28403     
28404     /**
28405      * Removes and destroys this item.
28406      */
28407     destroy : function(){
28408         this.td.parentNode.removeChild(this.td);
28409     },
28410     
28411     /**
28412      * Shows this item.
28413      */
28414     show: function(){
28415         this.hidden = false;
28416         this.td.style.display = "";
28417     },
28418     
28419     /**
28420      * Hides this item.
28421      */
28422     hide: function(){
28423         this.hidden = true;
28424         this.td.style.display = "none";
28425     },
28426     
28427     /**
28428      * Convenience function for boolean show/hide.
28429      * @param {Boolean} visible true to show/false to hide
28430      */
28431     setVisible: function(visible){
28432         if(visible) {
28433             this.show();
28434         }else{
28435             this.hide();
28436         }
28437     },
28438     
28439     /**
28440      * Try to focus this item.
28441      */
28442     focus : function(){
28443         Roo.fly(this.el).focus();
28444     },
28445     
28446     /**
28447      * Disables this item.
28448      */
28449     disable : function(){
28450         Roo.fly(this.td).addClass("x-item-disabled");
28451         this.disabled = true;
28452         this.el.disabled = true;
28453     },
28454     
28455     /**
28456      * Enables this item.
28457      */
28458     enable : function(){
28459         Roo.fly(this.td).removeClass("x-item-disabled");
28460         this.disabled = false;
28461         this.el.disabled = false;
28462     }
28463 };
28464
28465
28466 /**
28467  * @class Roo.Toolbar.Separator
28468  * @extends Roo.Toolbar.Item
28469  * A simple toolbar separator class
28470  * @constructor
28471  * Creates a new Separator
28472  */
28473 Roo.Toolbar.Separator = function(){
28474     var s = document.createElement("span");
28475     s.className = "ytb-sep";
28476     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28477 };
28478 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28479     enable:Roo.emptyFn,
28480     disable:Roo.emptyFn,
28481     focus:Roo.emptyFn
28482 });
28483
28484 /**
28485  * @class Roo.Toolbar.Spacer
28486  * @extends Roo.Toolbar.Item
28487  * A simple element that adds extra horizontal space to a toolbar.
28488  * @constructor
28489  * Creates a new Spacer
28490  */
28491 Roo.Toolbar.Spacer = function(){
28492     var s = document.createElement("div");
28493     s.className = "ytb-spacer";
28494     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28495 };
28496 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28497     enable:Roo.emptyFn,
28498     disable:Roo.emptyFn,
28499     focus:Roo.emptyFn
28500 });
28501
28502 /**
28503  * @class Roo.Toolbar.Fill
28504  * @extends Roo.Toolbar.Spacer
28505  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28506  * @constructor
28507  * Creates a new Spacer
28508  */
28509 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28510     // private
28511     render : function(td){
28512         td.style.width = '100%';
28513         Roo.Toolbar.Fill.superclass.render.call(this, td);
28514     }
28515 });
28516
28517 /**
28518  * @class Roo.Toolbar.TextItem
28519  * @extends Roo.Toolbar.Item
28520  * A simple class that renders text directly into a toolbar.
28521  * @constructor
28522  * Creates a new TextItem
28523  * @param {String} text
28524  */
28525 Roo.Toolbar.TextItem = function(text){
28526     if (typeof(text) == 'object') {
28527         text = text.text;
28528     }
28529     var s = document.createElement("span");
28530     s.className = "ytb-text";
28531     s.innerHTML = text;
28532     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28533 };
28534 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28535     enable:Roo.emptyFn,
28536     disable:Roo.emptyFn,
28537     focus:Roo.emptyFn
28538 });
28539
28540 /**
28541  * @class Roo.Toolbar.Button
28542  * @extends Roo.Button
28543  * A button that renders into a toolbar.
28544  * @constructor
28545  * Creates a new Button
28546  * @param {Object} config A standard {@link Roo.Button} config object
28547  */
28548 Roo.Toolbar.Button = function(config){
28549     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28550 };
28551 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28552     render : function(td){
28553         this.td = td;
28554         Roo.Toolbar.Button.superclass.render.call(this, td);
28555     },
28556     
28557     /**
28558      * Removes and destroys this button
28559      */
28560     destroy : function(){
28561         Roo.Toolbar.Button.superclass.destroy.call(this);
28562         this.td.parentNode.removeChild(this.td);
28563     },
28564     
28565     /**
28566      * Shows this button
28567      */
28568     show: function(){
28569         this.hidden = false;
28570         this.td.style.display = "";
28571     },
28572     
28573     /**
28574      * Hides this button
28575      */
28576     hide: function(){
28577         this.hidden = true;
28578         this.td.style.display = "none";
28579     },
28580
28581     /**
28582      * Disables this item
28583      */
28584     disable : function(){
28585         Roo.fly(this.td).addClass("x-item-disabled");
28586         this.disabled = true;
28587     },
28588
28589     /**
28590      * Enables this item
28591      */
28592     enable : function(){
28593         Roo.fly(this.td).removeClass("x-item-disabled");
28594         this.disabled = false;
28595     }
28596 });
28597 // backwards compat
28598 Roo.ToolbarButton = Roo.Toolbar.Button;
28599
28600 /**
28601  * @class Roo.Toolbar.SplitButton
28602  * @extends Roo.SplitButton
28603  * A menu button that renders into a toolbar.
28604  * @constructor
28605  * Creates a new SplitButton
28606  * @param {Object} config A standard {@link Roo.SplitButton} config object
28607  */
28608 Roo.Toolbar.SplitButton = function(config){
28609     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28610 };
28611 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28612     render : function(td){
28613         this.td = td;
28614         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28615     },
28616     
28617     /**
28618      * Removes and destroys this button
28619      */
28620     destroy : function(){
28621         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28622         this.td.parentNode.removeChild(this.td);
28623     },
28624     
28625     /**
28626      * Shows this button
28627      */
28628     show: function(){
28629         this.hidden = false;
28630         this.td.style.display = "";
28631     },
28632     
28633     /**
28634      * Hides this button
28635      */
28636     hide: function(){
28637         this.hidden = true;
28638         this.td.style.display = "none";
28639     }
28640 });
28641
28642 // backwards compat
28643 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28644  * Based on:
28645  * Ext JS Library 1.1.1
28646  * Copyright(c) 2006-2007, Ext JS, LLC.
28647  *
28648  * Originally Released Under LGPL - original licence link has changed is not relivant.
28649  *
28650  * Fork - LGPL
28651  * <script type="text/javascript">
28652  */
28653  
28654 /**
28655  * @class Roo.PagingToolbar
28656  * @extends Roo.Toolbar
28657  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28658  * @constructor
28659  * Create a new PagingToolbar
28660  * @param {Object} config The config object
28661  */
28662 Roo.PagingToolbar = function(el, ds, config)
28663 {
28664     // old args format still supported... - xtype is prefered..
28665     if (typeof(el) == 'object' && el.xtype) {
28666         // created from xtype...
28667         config = el;
28668         ds = el.dataSource;
28669         el = config.container;
28670     }
28671     var items = [];
28672     if (config.items) {
28673         items = config.items;
28674         config.items = [];
28675     }
28676     
28677     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28678     this.ds = ds;
28679     this.cursor = 0;
28680     this.renderButtons(this.el);
28681     this.bind(ds);
28682     
28683     // supprot items array.
28684    
28685     Roo.each(items, function(e) {
28686         this.add(Roo.factory(e));
28687     },this);
28688     
28689 };
28690
28691 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28692     /**
28693      * @cfg {Roo.data.Store} dataSource
28694      * The underlying data store providing the paged data
28695      */
28696     /**
28697      * @cfg {String/HTMLElement/Element} container
28698      * container The id or element that will contain the toolbar
28699      */
28700     /**
28701      * @cfg {Boolean} displayInfo
28702      * True to display the displayMsg (defaults to false)
28703      */
28704     /**
28705      * @cfg {Number} pageSize
28706      * The number of records to display per page (defaults to 20)
28707      */
28708     pageSize: 20,
28709     /**
28710      * @cfg {String} displayMsg
28711      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28712      */
28713     displayMsg : 'Displaying {0} - {1} of {2}',
28714     /**
28715      * @cfg {String} emptyMsg
28716      * The message to display when no records are found (defaults to "No data to display")
28717      */
28718     emptyMsg : 'No data to display',
28719     /**
28720      * Customizable piece of the default paging text (defaults to "Page")
28721      * @type String
28722      */
28723     beforePageText : "Page",
28724     /**
28725      * Customizable piece of the default paging text (defaults to "of %0")
28726      * @type String
28727      */
28728     afterPageText : "of {0}",
28729     /**
28730      * Customizable piece of the default paging text (defaults to "First Page")
28731      * @type String
28732      */
28733     firstText : "First Page",
28734     /**
28735      * Customizable piece of the default paging text (defaults to "Previous Page")
28736      * @type String
28737      */
28738     prevText : "Previous Page",
28739     /**
28740      * Customizable piece of the default paging text (defaults to "Next Page")
28741      * @type String
28742      */
28743     nextText : "Next Page",
28744     /**
28745      * Customizable piece of the default paging text (defaults to "Last Page")
28746      * @type String
28747      */
28748     lastText : "Last Page",
28749     /**
28750      * Customizable piece of the default paging text (defaults to "Refresh")
28751      * @type String
28752      */
28753     refreshText : "Refresh",
28754
28755     // private
28756     renderButtons : function(el){
28757         Roo.PagingToolbar.superclass.render.call(this, el);
28758         this.first = this.addButton({
28759             tooltip: this.firstText,
28760             cls: "x-btn-icon x-grid-page-first",
28761             disabled: true,
28762             handler: this.onClick.createDelegate(this, ["first"])
28763         });
28764         this.prev = this.addButton({
28765             tooltip: this.prevText,
28766             cls: "x-btn-icon x-grid-page-prev",
28767             disabled: true,
28768             handler: this.onClick.createDelegate(this, ["prev"])
28769         });
28770         //this.addSeparator();
28771         this.add(this.beforePageText);
28772         this.field = Roo.get(this.addDom({
28773            tag: "input",
28774            type: "text",
28775            size: "3",
28776            value: "1",
28777            cls: "x-grid-page-number"
28778         }).el);
28779         this.field.on("keydown", this.onPagingKeydown, this);
28780         this.field.on("focus", function(){this.dom.select();});
28781         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28782         this.field.setHeight(18);
28783         //this.addSeparator();
28784         this.next = this.addButton({
28785             tooltip: this.nextText,
28786             cls: "x-btn-icon x-grid-page-next",
28787             disabled: true,
28788             handler: this.onClick.createDelegate(this, ["next"])
28789         });
28790         this.last = this.addButton({
28791             tooltip: this.lastText,
28792             cls: "x-btn-icon x-grid-page-last",
28793             disabled: true,
28794             handler: this.onClick.createDelegate(this, ["last"])
28795         });
28796         //this.addSeparator();
28797         this.loading = this.addButton({
28798             tooltip: this.refreshText,
28799             cls: "x-btn-icon x-grid-loading",
28800             handler: this.onClick.createDelegate(this, ["refresh"])
28801         });
28802
28803         if(this.displayInfo){
28804             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28805         }
28806     },
28807
28808     // private
28809     updateInfo : function(){
28810         if(this.displayEl){
28811             var count = this.ds.getCount();
28812             var msg = count == 0 ?
28813                 this.emptyMsg :
28814                 String.format(
28815                     this.displayMsg,
28816                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28817                 );
28818             this.displayEl.update(msg);
28819         }
28820     },
28821
28822     // private
28823     onLoad : function(ds, r, o){
28824        this.cursor = o.params ? o.params.start : 0;
28825        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28826
28827        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28828        this.field.dom.value = ap;
28829        this.first.setDisabled(ap == 1);
28830        this.prev.setDisabled(ap == 1);
28831        this.next.setDisabled(ap == ps);
28832        this.last.setDisabled(ap == ps);
28833        this.loading.enable();
28834        this.updateInfo();
28835     },
28836
28837     // private
28838     getPageData : function(){
28839         var total = this.ds.getTotalCount();
28840         return {
28841             total : total,
28842             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28843             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28844         };
28845     },
28846
28847     // private
28848     onLoadError : function(){
28849         this.loading.enable();
28850     },
28851
28852     // private
28853     onPagingKeydown : function(e){
28854         var k = e.getKey();
28855         var d = this.getPageData();
28856         if(k == e.RETURN){
28857             var v = this.field.dom.value, pageNum;
28858             if(!v || isNaN(pageNum = parseInt(v, 10))){
28859                 this.field.dom.value = d.activePage;
28860                 return;
28861             }
28862             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28863             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28864             e.stopEvent();
28865         }
28866         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))
28867         {
28868           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28869           this.field.dom.value = pageNum;
28870           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28871           e.stopEvent();
28872         }
28873         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28874         {
28875           var v = this.field.dom.value, pageNum; 
28876           var increment = (e.shiftKey) ? 10 : 1;
28877           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28878             increment *= -1;
28879           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28880             this.field.dom.value = d.activePage;
28881             return;
28882           }
28883           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28884           {
28885             this.field.dom.value = parseInt(v, 10) + increment;
28886             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28887             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28888           }
28889           e.stopEvent();
28890         }
28891     },
28892
28893     // private
28894     beforeLoad : function(){
28895         if(this.loading){
28896             this.loading.disable();
28897         }
28898     },
28899
28900     // private
28901     onClick : function(which){
28902         var ds = this.ds;
28903         switch(which){
28904             case "first":
28905                 ds.load({params:{start: 0, limit: this.pageSize}});
28906             break;
28907             case "prev":
28908                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28909             break;
28910             case "next":
28911                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28912             break;
28913             case "last":
28914                 var total = ds.getTotalCount();
28915                 var extra = total % this.pageSize;
28916                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28917                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28918             break;
28919             case "refresh":
28920                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28921             break;
28922         }
28923     },
28924
28925     /**
28926      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28927      * @param {Roo.data.Store} store The data store to unbind
28928      */
28929     unbind : function(ds){
28930         ds.un("beforeload", this.beforeLoad, this);
28931         ds.un("load", this.onLoad, this);
28932         ds.un("loadexception", this.onLoadError, this);
28933         ds.un("remove", this.updateInfo, this);
28934         ds.un("add", this.updateInfo, this);
28935         this.ds = undefined;
28936     },
28937
28938     /**
28939      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28940      * @param {Roo.data.Store} store The data store to bind
28941      */
28942     bind : function(ds){
28943         ds.on("beforeload", this.beforeLoad, this);
28944         ds.on("load", this.onLoad, this);
28945         ds.on("loadexception", this.onLoadError, this);
28946         ds.on("remove", this.updateInfo, this);
28947         ds.on("add", this.updateInfo, this);
28948         this.ds = ds;
28949     }
28950 });/*
28951  * Based on:
28952  * Ext JS Library 1.1.1
28953  * Copyright(c) 2006-2007, Ext JS, LLC.
28954  *
28955  * Originally Released Under LGPL - original licence link has changed is not relivant.
28956  *
28957  * Fork - LGPL
28958  * <script type="text/javascript">
28959  */
28960
28961 /**
28962  * @class Roo.Resizable
28963  * @extends Roo.util.Observable
28964  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28965  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28966  * 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
28967  * the element will be wrapped for you automatically.</p>
28968  * <p>Here is the list of valid resize handles:</p>
28969  * <pre>
28970 Value   Description
28971 ------  -------------------
28972  'n'     north
28973  's'     south
28974  'e'     east
28975  'w'     west
28976  'nw'    northwest
28977  'sw'    southwest
28978  'se'    southeast
28979  'ne'    northeast
28980  'hd'    horizontal drag
28981  'all'   all
28982 </pre>
28983  * <p>Here's an example showing the creation of a typical Resizable:</p>
28984  * <pre><code>
28985 var resizer = new Roo.Resizable("element-id", {
28986     handles: 'all',
28987     minWidth: 200,
28988     minHeight: 100,
28989     maxWidth: 500,
28990     maxHeight: 400,
28991     pinned: true
28992 });
28993 resizer.on("resize", myHandler);
28994 </code></pre>
28995  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28996  * resizer.east.setDisplayed(false);</p>
28997  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28998  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28999  * resize operation's new size (defaults to [0, 0])
29000  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29001  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29002  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29003  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29004  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29005  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29006  * @cfg {Number} width The width of the element in pixels (defaults to null)
29007  * @cfg {Number} height The height of the element in pixels (defaults to null)
29008  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29009  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29010  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29011  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29012  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29013  * in favor of the handles config option (defaults to false)
29014  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29015  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29016  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29017  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29018  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29019  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29020  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29021  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29022  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29023  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29024  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29025  * @constructor
29026  * Create a new resizable component
29027  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29028  * @param {Object} config configuration options
29029   */
29030 Roo.Resizable = function(el, config)
29031 {
29032     this.el = Roo.get(el);
29033
29034     if(config && config.wrap){
29035         config.resizeChild = this.el;
29036         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29037         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29038         this.el.setStyle("overflow", "hidden");
29039         this.el.setPositioning(config.resizeChild.getPositioning());
29040         config.resizeChild.clearPositioning();
29041         if(!config.width || !config.height){
29042             var csize = config.resizeChild.getSize();
29043             this.el.setSize(csize.width, csize.height);
29044         }
29045         if(config.pinned && !config.adjustments){
29046             config.adjustments = "auto";
29047         }
29048     }
29049
29050     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29051     this.proxy.unselectable();
29052     this.proxy.enableDisplayMode('block');
29053
29054     Roo.apply(this, config);
29055
29056     if(this.pinned){
29057         this.disableTrackOver = true;
29058         this.el.addClass("x-resizable-pinned");
29059     }
29060     // if the element isn't positioned, make it relative
29061     var position = this.el.getStyle("position");
29062     if(position != "absolute" && position != "fixed"){
29063         this.el.setStyle("position", "relative");
29064     }
29065     if(!this.handles){ // no handles passed, must be legacy style
29066         this.handles = 's,e,se';
29067         if(this.multiDirectional){
29068             this.handles += ',n,w';
29069         }
29070     }
29071     if(this.handles == "all"){
29072         this.handles = "n s e w ne nw se sw";
29073     }
29074     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29075     var ps = Roo.Resizable.positions;
29076     for(var i = 0, len = hs.length; i < len; i++){
29077         if(hs[i] && ps[hs[i]]){
29078             var pos = ps[hs[i]];
29079             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29080         }
29081     }
29082     // legacy
29083     this.corner = this.southeast;
29084     
29085     // updateBox = the box can move..
29086     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29087         this.updateBox = true;
29088     }
29089
29090     this.activeHandle = null;
29091
29092     if(this.resizeChild){
29093         if(typeof this.resizeChild == "boolean"){
29094             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29095         }else{
29096             this.resizeChild = Roo.get(this.resizeChild, true);
29097         }
29098     }
29099     
29100     if(this.adjustments == "auto"){
29101         var rc = this.resizeChild;
29102         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29103         if(rc && (hw || hn)){
29104             rc.position("relative");
29105             rc.setLeft(hw ? hw.el.getWidth() : 0);
29106             rc.setTop(hn ? hn.el.getHeight() : 0);
29107         }
29108         this.adjustments = [
29109             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29110             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29111         ];
29112     }
29113
29114     if(this.draggable){
29115         this.dd = this.dynamic ?
29116             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29117         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29118     }
29119
29120     // public events
29121     this.addEvents({
29122         /**
29123          * @event beforeresize
29124          * Fired before resize is allowed. Set enabled to false to cancel resize.
29125          * @param {Roo.Resizable} this
29126          * @param {Roo.EventObject} e The mousedown event
29127          */
29128         "beforeresize" : true,
29129         /**
29130          * @event resizing
29131          * Fired a resizing.
29132          * @param {Roo.Resizable} this
29133          * @param {Number} x The new x position
29134          * @param {Number} y The new y position
29135          * @param {Number} w The new w width
29136          * @param {Number} h The new h hight
29137          * @param {Roo.EventObject} e The mouseup event
29138          */
29139         "resizing" : true,
29140         /**
29141          * @event resize
29142          * Fired after a resize.
29143          * @param {Roo.Resizable} this
29144          * @param {Number} width The new width
29145          * @param {Number} height The new height
29146          * @param {Roo.EventObject} e The mouseup event
29147          */
29148         "resize" : true
29149     });
29150
29151     if(this.width !== null && this.height !== null){
29152         this.resizeTo(this.width, this.height);
29153     }else{
29154         this.updateChildSize();
29155     }
29156     if(Roo.isIE){
29157         this.el.dom.style.zoom = 1;
29158     }
29159     Roo.Resizable.superclass.constructor.call(this);
29160 };
29161
29162 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29163         resizeChild : false,
29164         adjustments : [0, 0],
29165         minWidth : 5,
29166         minHeight : 5,
29167         maxWidth : 10000,
29168         maxHeight : 10000,
29169         enabled : true,
29170         animate : false,
29171         duration : .35,
29172         dynamic : false,
29173         handles : false,
29174         multiDirectional : false,
29175         disableTrackOver : false,
29176         easing : 'easeOutStrong',
29177         widthIncrement : 0,
29178         heightIncrement : 0,
29179         pinned : false,
29180         width : null,
29181         height : null,
29182         preserveRatio : false,
29183         transparent: false,
29184         minX: 0,
29185         minY: 0,
29186         draggable: false,
29187
29188         /**
29189          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29190          */
29191         constrainTo: undefined,
29192         /**
29193          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29194          */
29195         resizeRegion: undefined,
29196
29197
29198     /**
29199      * Perform a manual resize
29200      * @param {Number} width
29201      * @param {Number} height
29202      */
29203     resizeTo : function(width, height){
29204         this.el.setSize(width, height);
29205         this.updateChildSize();
29206         this.fireEvent("resize", this, width, height, null);
29207     },
29208
29209     // private
29210     startSizing : function(e, handle){
29211         this.fireEvent("beforeresize", this, e);
29212         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29213
29214             if(!this.overlay){
29215                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29216                 this.overlay.unselectable();
29217                 this.overlay.enableDisplayMode("block");
29218                 this.overlay.on("mousemove", this.onMouseMove, this);
29219                 this.overlay.on("mouseup", this.onMouseUp, this);
29220             }
29221             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29222
29223             this.resizing = true;
29224             this.startBox = this.el.getBox();
29225             this.startPoint = e.getXY();
29226             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29227                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29228
29229             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29230             this.overlay.show();
29231
29232             if(this.constrainTo) {
29233                 var ct = Roo.get(this.constrainTo);
29234                 this.resizeRegion = ct.getRegion().adjust(
29235                     ct.getFrameWidth('t'),
29236                     ct.getFrameWidth('l'),
29237                     -ct.getFrameWidth('b'),
29238                     -ct.getFrameWidth('r')
29239                 );
29240             }
29241
29242             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29243             this.proxy.show();
29244             this.proxy.setBox(this.startBox);
29245             if(!this.dynamic){
29246                 this.proxy.setStyle('visibility', 'visible');
29247             }
29248         }
29249     },
29250
29251     // private
29252     onMouseDown : function(handle, e){
29253         if(this.enabled){
29254             e.stopEvent();
29255             this.activeHandle = handle;
29256             this.startSizing(e, handle);
29257         }
29258     },
29259
29260     // private
29261     onMouseUp : function(e){
29262         var size = this.resizeElement();
29263         this.resizing = false;
29264         this.handleOut();
29265         this.overlay.hide();
29266         this.proxy.hide();
29267         this.fireEvent("resize", this, size.width, size.height, e);
29268     },
29269
29270     // private
29271     updateChildSize : function(){
29272         
29273         if(this.resizeChild){
29274             var el = this.el;
29275             var child = this.resizeChild;
29276             var adj = this.adjustments;
29277             if(el.dom.offsetWidth){
29278                 var b = el.getSize(true);
29279                 child.setSize(b.width+adj[0], b.height+adj[1]);
29280             }
29281             // Second call here for IE
29282             // The first call enables instant resizing and
29283             // the second call corrects scroll bars if they
29284             // exist
29285             if(Roo.isIE){
29286                 setTimeout(function(){
29287                     if(el.dom.offsetWidth){
29288                         var b = el.getSize(true);
29289                         child.setSize(b.width+adj[0], b.height+adj[1]);
29290                     }
29291                 }, 10);
29292             }
29293         }
29294     },
29295
29296     // private
29297     snap : function(value, inc, min){
29298         if(!inc || !value) return value;
29299         var newValue = value;
29300         var m = value % inc;
29301         if(m > 0){
29302             if(m > (inc/2)){
29303                 newValue = value + (inc-m);
29304             }else{
29305                 newValue = value - m;
29306             }
29307         }
29308         return Math.max(min, newValue);
29309     },
29310
29311     // private
29312     resizeElement : function(){
29313         var box = this.proxy.getBox();
29314         if(this.updateBox){
29315             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29316         }else{
29317             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29318         }
29319         this.updateChildSize();
29320         if(!this.dynamic){
29321             this.proxy.hide();
29322         }
29323         return box;
29324     },
29325
29326     // private
29327     constrain : function(v, diff, m, mx){
29328         if(v - diff < m){
29329             diff = v - m;
29330         }else if(v - diff > mx){
29331             diff = mx - v;
29332         }
29333         return diff;
29334     },
29335
29336     // private
29337     onMouseMove : function(e){
29338         
29339         if(this.enabled){
29340             try{// try catch so if something goes wrong the user doesn't get hung
29341
29342             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29343                 return;
29344             }
29345
29346             //var curXY = this.startPoint;
29347             var curSize = this.curSize || this.startBox;
29348             var x = this.startBox.x, y = this.startBox.y;
29349             var ox = x, oy = y;
29350             var w = curSize.width, h = curSize.height;
29351             var ow = w, oh = h;
29352             var mw = this.minWidth, mh = this.minHeight;
29353             var mxw = this.maxWidth, mxh = this.maxHeight;
29354             var wi = this.widthIncrement;
29355             var hi = this.heightIncrement;
29356
29357             var eventXY = e.getXY();
29358             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29359             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29360
29361             var pos = this.activeHandle.position;
29362
29363             switch(pos){
29364                 case "east":
29365                     w += diffX;
29366                     w = Math.min(Math.max(mw, w), mxw);
29367                     break;
29368              
29369                 case "south":
29370                     h += diffY;
29371                     h = Math.min(Math.max(mh, h), mxh);
29372                     break;
29373                 case "southeast":
29374                     w += diffX;
29375                     h += diffY;
29376                     w = Math.min(Math.max(mw, w), mxw);
29377                     h = Math.min(Math.max(mh, h), mxh);
29378                     break;
29379                 case "north":
29380                     diffY = this.constrain(h, diffY, mh, mxh);
29381                     y += diffY;
29382                     h -= diffY;
29383                     break;
29384                 case "hdrag":
29385                     
29386                     if (wi) {
29387                         var adiffX = Math.abs(diffX);
29388                         var sub = (adiffX % wi); // how much 
29389                         if (sub > (wi/2)) { // far enough to snap
29390                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29391                         } else {
29392                             // remove difference.. 
29393                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29394                         }
29395                     }
29396                     x += diffX;
29397                     x = Math.max(this.minX, x);
29398                     break;
29399                 case "west":
29400                     diffX = this.constrain(w, diffX, mw, mxw);
29401                     x += diffX;
29402                     w -= diffX;
29403                     break;
29404                 case "northeast":
29405                     w += diffX;
29406                     w = Math.min(Math.max(mw, w), mxw);
29407                     diffY = this.constrain(h, diffY, mh, mxh);
29408                     y += diffY;
29409                     h -= diffY;
29410                     break;
29411                 case "northwest":
29412                     diffX = this.constrain(w, diffX, mw, mxw);
29413                     diffY = this.constrain(h, diffY, mh, mxh);
29414                     y += diffY;
29415                     h -= diffY;
29416                     x += diffX;
29417                     w -= diffX;
29418                     break;
29419                case "southwest":
29420                     diffX = this.constrain(w, diffX, mw, mxw);
29421                     h += diffY;
29422                     h = Math.min(Math.max(mh, h), mxh);
29423                     x += diffX;
29424                     w -= diffX;
29425                     break;
29426             }
29427
29428             var sw = this.snap(w, wi, mw);
29429             var sh = this.snap(h, hi, mh);
29430             if(sw != w || sh != h){
29431                 switch(pos){
29432                     case "northeast":
29433                         y -= sh - h;
29434                     break;
29435                     case "north":
29436                         y -= sh - h;
29437                         break;
29438                     case "southwest":
29439                         x -= sw - w;
29440                     break;
29441                     case "west":
29442                         x -= sw - w;
29443                         break;
29444                     case "northwest":
29445                         x -= sw - w;
29446                         y -= sh - h;
29447                     break;
29448                 }
29449                 w = sw;
29450                 h = sh;
29451             }
29452
29453             if(this.preserveRatio){
29454                 switch(pos){
29455                     case "southeast":
29456                     case "east":
29457                         h = oh * (w/ow);
29458                         h = Math.min(Math.max(mh, h), mxh);
29459                         w = ow * (h/oh);
29460                        break;
29461                     case "south":
29462                         w = ow * (h/oh);
29463                         w = Math.min(Math.max(mw, w), mxw);
29464                         h = oh * (w/ow);
29465                         break;
29466                     case "northeast":
29467                         w = ow * (h/oh);
29468                         w = Math.min(Math.max(mw, w), mxw);
29469                         h = oh * (w/ow);
29470                     break;
29471                     case "north":
29472                         var tw = w;
29473                         w = ow * (h/oh);
29474                         w = Math.min(Math.max(mw, w), mxw);
29475                         h = oh * (w/ow);
29476                         x += (tw - w) / 2;
29477                         break;
29478                     case "southwest":
29479                         h = oh * (w/ow);
29480                         h = Math.min(Math.max(mh, h), mxh);
29481                         var tw = w;
29482                         w = ow * (h/oh);
29483                         x += tw - w;
29484                         break;
29485                     case "west":
29486                         var th = h;
29487                         h = oh * (w/ow);
29488                         h = Math.min(Math.max(mh, h), mxh);
29489                         y += (th - h) / 2;
29490                         var tw = w;
29491                         w = ow * (h/oh);
29492                         x += tw - w;
29493                        break;
29494                     case "northwest":
29495                         var tw = w;
29496                         var th = h;
29497                         h = oh * (w/ow);
29498                         h = Math.min(Math.max(mh, h), mxh);
29499                         w = ow * (h/oh);
29500                         y += th - h;
29501                         x += tw - w;
29502                        break;
29503
29504                 }
29505             }
29506             if (pos == 'hdrag') {
29507                 w = ow;
29508             }
29509             this.proxy.setBounds(x, y, w, h);
29510             if(this.dynamic){
29511                 this.resizeElement();
29512             }
29513             }catch(e){}
29514         }
29515         this.fireEvent("resizing", this, x, y, w, h, e);
29516     },
29517
29518     // private
29519     handleOver : function(){
29520         if(this.enabled){
29521             this.el.addClass("x-resizable-over");
29522         }
29523     },
29524
29525     // private
29526     handleOut : function(){
29527         if(!this.resizing){
29528             this.el.removeClass("x-resizable-over");
29529         }
29530     },
29531
29532     /**
29533      * Returns the element this component is bound to.
29534      * @return {Roo.Element}
29535      */
29536     getEl : function(){
29537         return this.el;
29538     },
29539
29540     /**
29541      * Returns the resizeChild element (or null).
29542      * @return {Roo.Element}
29543      */
29544     getResizeChild : function(){
29545         return this.resizeChild;
29546     },
29547     groupHandler : function()
29548     {
29549         
29550     },
29551     /**
29552      * Destroys this resizable. If the element was wrapped and
29553      * removeEl is not true then the element remains.
29554      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29555      */
29556     destroy : function(removeEl){
29557         this.proxy.remove();
29558         if(this.overlay){
29559             this.overlay.removeAllListeners();
29560             this.overlay.remove();
29561         }
29562         var ps = Roo.Resizable.positions;
29563         for(var k in ps){
29564             if(typeof ps[k] != "function" && this[ps[k]]){
29565                 var h = this[ps[k]];
29566                 h.el.removeAllListeners();
29567                 h.el.remove();
29568             }
29569         }
29570         if(removeEl){
29571             this.el.update("");
29572             this.el.remove();
29573         }
29574     }
29575 });
29576
29577 // private
29578 // hash to map config positions to true positions
29579 Roo.Resizable.positions = {
29580     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29581     hd: "hdrag"
29582 };
29583
29584 // private
29585 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29586     if(!this.tpl){
29587         // only initialize the template if resizable is used
29588         var tpl = Roo.DomHelper.createTemplate(
29589             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29590         );
29591         tpl.compile();
29592         Roo.Resizable.Handle.prototype.tpl = tpl;
29593     }
29594     this.position = pos;
29595     this.rz = rz;
29596     // show north drag fro topdra
29597     var handlepos = pos == 'hdrag' ? 'north' : pos;
29598     
29599     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29600     if (pos == 'hdrag') {
29601         this.el.setStyle('cursor', 'pointer');
29602     }
29603     this.el.unselectable();
29604     if(transparent){
29605         this.el.setOpacity(0);
29606     }
29607     this.el.on("mousedown", this.onMouseDown, this);
29608     if(!disableTrackOver){
29609         this.el.on("mouseover", this.onMouseOver, this);
29610         this.el.on("mouseout", this.onMouseOut, this);
29611     }
29612 };
29613
29614 // private
29615 Roo.Resizable.Handle.prototype = {
29616     afterResize : function(rz){
29617         Roo.log('after?');
29618         // do nothing
29619     },
29620     // private
29621     onMouseDown : function(e){
29622         this.rz.onMouseDown(this, e);
29623     },
29624     // private
29625     onMouseOver : function(e){
29626         this.rz.handleOver(this, e);
29627     },
29628     // private
29629     onMouseOut : function(e){
29630         this.rz.handleOut(this, e);
29631     }
29632 };/*
29633  * Based on:
29634  * Ext JS Library 1.1.1
29635  * Copyright(c) 2006-2007, Ext JS, LLC.
29636  *
29637  * Originally Released Under LGPL - original licence link has changed is not relivant.
29638  *
29639  * Fork - LGPL
29640  * <script type="text/javascript">
29641  */
29642
29643 /**
29644  * @class Roo.Editor
29645  * @extends Roo.Component
29646  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29647  * @constructor
29648  * Create a new Editor
29649  * @param {Roo.form.Field} field The Field object (or descendant)
29650  * @param {Object} config The config object
29651  */
29652 Roo.Editor = function(field, config){
29653     Roo.Editor.superclass.constructor.call(this, config);
29654     this.field = field;
29655     this.addEvents({
29656         /**
29657              * @event beforestartedit
29658              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29659              * false from the handler of this event.
29660              * @param {Editor} this
29661              * @param {Roo.Element} boundEl The underlying element bound to this editor
29662              * @param {Mixed} value The field value being set
29663              */
29664         "beforestartedit" : true,
29665         /**
29666              * @event startedit
29667              * Fires when this editor is displayed
29668              * @param {Roo.Element} boundEl The underlying element bound to this editor
29669              * @param {Mixed} value The starting field value
29670              */
29671         "startedit" : true,
29672         /**
29673              * @event beforecomplete
29674              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29675              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29676              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29677              * event will not fire since no edit actually occurred.
29678              * @param {Editor} this
29679              * @param {Mixed} value The current field value
29680              * @param {Mixed} startValue The original field value
29681              */
29682         "beforecomplete" : true,
29683         /**
29684              * @event complete
29685              * Fires after editing is complete and any changed value has been written to the underlying field.
29686              * @param {Editor} this
29687              * @param {Mixed} value The current field value
29688              * @param {Mixed} startValue The original field value
29689              */
29690         "complete" : true,
29691         /**
29692          * @event specialkey
29693          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29694          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29695          * @param {Roo.form.Field} this
29696          * @param {Roo.EventObject} e The event object
29697          */
29698         "specialkey" : true
29699     });
29700 };
29701
29702 Roo.extend(Roo.Editor, Roo.Component, {
29703     /**
29704      * @cfg {Boolean/String} autosize
29705      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29706      * or "height" to adopt the height only (defaults to false)
29707      */
29708     /**
29709      * @cfg {Boolean} revertInvalid
29710      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29711      * validation fails (defaults to true)
29712      */
29713     /**
29714      * @cfg {Boolean} ignoreNoChange
29715      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29716      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29717      * will never be ignored.
29718      */
29719     /**
29720      * @cfg {Boolean} hideEl
29721      * False to keep the bound element visible while the editor is displayed (defaults to true)
29722      */
29723     /**
29724      * @cfg {Mixed} value
29725      * The data value of the underlying field (defaults to "")
29726      */
29727     value : "",
29728     /**
29729      * @cfg {String} alignment
29730      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29731      */
29732     alignment: "c-c?",
29733     /**
29734      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29735      * for bottom-right shadow (defaults to "frame")
29736      */
29737     shadow : "frame",
29738     /**
29739      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29740      */
29741     constrain : false,
29742     /**
29743      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29744      */
29745     completeOnEnter : false,
29746     /**
29747      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29748      */
29749     cancelOnEsc : false,
29750     /**
29751      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29752      */
29753     updateEl : false,
29754
29755     // private
29756     onRender : function(ct, position){
29757         this.el = new Roo.Layer({
29758             shadow: this.shadow,
29759             cls: "x-editor",
29760             parentEl : ct,
29761             shim : this.shim,
29762             shadowOffset:4,
29763             id: this.id,
29764             constrain: this.constrain
29765         });
29766         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29767         if(this.field.msgTarget != 'title'){
29768             this.field.msgTarget = 'qtip';
29769         }
29770         this.field.render(this.el);
29771         if(Roo.isGecko){
29772             this.field.el.dom.setAttribute('autocomplete', 'off');
29773         }
29774         this.field.on("specialkey", this.onSpecialKey, this);
29775         if(this.swallowKeys){
29776             this.field.el.swallowEvent(['keydown','keypress']);
29777         }
29778         this.field.show();
29779         this.field.on("blur", this.onBlur, this);
29780         if(this.field.grow){
29781             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29782         }
29783     },
29784
29785     onSpecialKey : function(field, e)
29786     {
29787         //Roo.log('editor onSpecialKey');
29788         if(this.completeOnEnter && e.getKey() == e.ENTER){
29789             e.stopEvent();
29790             this.completeEdit();
29791             return;
29792         }
29793         // do not fire special key otherwise it might hide close the editor...
29794         if(e.getKey() == e.ENTER){    
29795             return;
29796         }
29797         if(this.cancelOnEsc && e.getKey() == e.ESC){
29798             this.cancelEdit();
29799             return;
29800         } 
29801         this.fireEvent('specialkey', field, e);
29802     
29803     },
29804
29805     /**
29806      * Starts the editing process and shows the editor.
29807      * @param {String/HTMLElement/Element} el The element to edit
29808      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29809       * to the innerHTML of el.
29810      */
29811     startEdit : function(el, value){
29812         if(this.editing){
29813             this.completeEdit();
29814         }
29815         this.boundEl = Roo.get(el);
29816         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29817         if(!this.rendered){
29818             this.render(this.parentEl || document.body);
29819         }
29820         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29821             return;
29822         }
29823         this.startValue = v;
29824         this.field.setValue(v);
29825         if(this.autoSize){
29826             var sz = this.boundEl.getSize();
29827             switch(this.autoSize){
29828                 case "width":
29829                 this.setSize(sz.width,  "");
29830                 break;
29831                 case "height":
29832                 this.setSize("",  sz.height);
29833                 break;
29834                 default:
29835                 this.setSize(sz.width,  sz.height);
29836             }
29837         }
29838         this.el.alignTo(this.boundEl, this.alignment);
29839         this.editing = true;
29840         if(Roo.QuickTips){
29841             Roo.QuickTips.disable();
29842         }
29843         this.show();
29844     },
29845
29846     /**
29847      * Sets the height and width of this editor.
29848      * @param {Number} width The new width
29849      * @param {Number} height The new height
29850      */
29851     setSize : function(w, h){
29852         this.field.setSize(w, h);
29853         if(this.el){
29854             this.el.sync();
29855         }
29856     },
29857
29858     /**
29859      * Realigns the editor to the bound field based on the current alignment config value.
29860      */
29861     realign : function(){
29862         this.el.alignTo(this.boundEl, this.alignment);
29863     },
29864
29865     /**
29866      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29867      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29868      */
29869     completeEdit : function(remainVisible){
29870         if(!this.editing){
29871             return;
29872         }
29873         var v = this.getValue();
29874         if(this.revertInvalid !== false && !this.field.isValid()){
29875             v = this.startValue;
29876             this.cancelEdit(true);
29877         }
29878         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29879             this.editing = false;
29880             this.hide();
29881             return;
29882         }
29883         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29884             this.editing = false;
29885             if(this.updateEl && this.boundEl){
29886                 this.boundEl.update(v);
29887             }
29888             if(remainVisible !== true){
29889                 this.hide();
29890             }
29891             this.fireEvent("complete", this, v, this.startValue);
29892         }
29893     },
29894
29895     // private
29896     onShow : function(){
29897         this.el.show();
29898         if(this.hideEl !== false){
29899             this.boundEl.hide();
29900         }
29901         this.field.show();
29902         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29903             this.fixIEFocus = true;
29904             this.deferredFocus.defer(50, this);
29905         }else{
29906             this.field.focus();
29907         }
29908         this.fireEvent("startedit", this.boundEl, this.startValue);
29909     },
29910
29911     deferredFocus : function(){
29912         if(this.editing){
29913             this.field.focus();
29914         }
29915     },
29916
29917     /**
29918      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29919      * reverted to the original starting value.
29920      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29921      * cancel (defaults to false)
29922      */
29923     cancelEdit : function(remainVisible){
29924         if(this.editing){
29925             this.setValue(this.startValue);
29926             if(remainVisible !== true){
29927                 this.hide();
29928             }
29929         }
29930     },
29931
29932     // private
29933     onBlur : function(){
29934         if(this.allowBlur !== true && this.editing){
29935             this.completeEdit();
29936         }
29937     },
29938
29939     // private
29940     onHide : function(){
29941         if(this.editing){
29942             this.completeEdit();
29943             return;
29944         }
29945         this.field.blur();
29946         if(this.field.collapse){
29947             this.field.collapse();
29948         }
29949         this.el.hide();
29950         if(this.hideEl !== false){
29951             this.boundEl.show();
29952         }
29953         if(Roo.QuickTips){
29954             Roo.QuickTips.enable();
29955         }
29956     },
29957
29958     /**
29959      * Sets the data value of the editor
29960      * @param {Mixed} value Any valid value supported by the underlying field
29961      */
29962     setValue : function(v){
29963         this.field.setValue(v);
29964     },
29965
29966     /**
29967      * Gets the data value of the editor
29968      * @return {Mixed} The data value
29969      */
29970     getValue : function(){
29971         return this.field.getValue();
29972     }
29973 });/*
29974  * Based on:
29975  * Ext JS Library 1.1.1
29976  * Copyright(c) 2006-2007, Ext JS, LLC.
29977  *
29978  * Originally Released Under LGPL - original licence link has changed is not relivant.
29979  *
29980  * Fork - LGPL
29981  * <script type="text/javascript">
29982  */
29983  
29984 /**
29985  * @class Roo.BasicDialog
29986  * @extends Roo.util.Observable
29987  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29988  * <pre><code>
29989 var dlg = new Roo.BasicDialog("my-dlg", {
29990     height: 200,
29991     width: 300,
29992     minHeight: 100,
29993     minWidth: 150,
29994     modal: true,
29995     proxyDrag: true,
29996     shadow: true
29997 });
29998 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29999 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30000 dlg.addButton('Cancel', dlg.hide, dlg);
30001 dlg.show();
30002 </code></pre>
30003   <b>A Dialog should always be a direct child of the body element.</b>
30004  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30005  * @cfg {String} title Default text to display in the title bar (defaults to null)
30006  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30007  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30008  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30009  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30010  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30011  * (defaults to null with no animation)
30012  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30013  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30014  * property for valid values (defaults to 'all')
30015  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30016  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30017  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30018  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30019  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30020  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30021  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30022  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30023  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30024  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30025  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30026  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30027  * draggable = true (defaults to false)
30028  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30029  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30030  * shadow (defaults to false)
30031  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30032  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30033  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30034  * @cfg {Array} buttons Array of buttons
30035  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30036  * @constructor
30037  * Create a new BasicDialog.
30038  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30039  * @param {Object} config Configuration options
30040  */
30041 Roo.BasicDialog = function(el, config){
30042     this.el = Roo.get(el);
30043     var dh = Roo.DomHelper;
30044     if(!this.el && config && config.autoCreate){
30045         if(typeof config.autoCreate == "object"){
30046             if(!config.autoCreate.id){
30047                 config.autoCreate.id = el;
30048             }
30049             this.el = dh.append(document.body,
30050                         config.autoCreate, true);
30051         }else{
30052             this.el = dh.append(document.body,
30053                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30054         }
30055     }
30056     el = this.el;
30057     el.setDisplayed(true);
30058     el.hide = this.hideAction;
30059     this.id = el.id;
30060     el.addClass("x-dlg");
30061
30062     Roo.apply(this, config);
30063
30064     this.proxy = el.createProxy("x-dlg-proxy");
30065     this.proxy.hide = this.hideAction;
30066     this.proxy.setOpacity(.5);
30067     this.proxy.hide();
30068
30069     if(config.width){
30070         el.setWidth(config.width);
30071     }
30072     if(config.height){
30073         el.setHeight(config.height);
30074     }
30075     this.size = el.getSize();
30076     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30077         this.xy = [config.x,config.y];
30078     }else{
30079         this.xy = el.getCenterXY(true);
30080     }
30081     /** The header element @type Roo.Element */
30082     this.header = el.child("> .x-dlg-hd");
30083     /** The body element @type Roo.Element */
30084     this.body = el.child("> .x-dlg-bd");
30085     /** The footer element @type Roo.Element */
30086     this.footer = el.child("> .x-dlg-ft");
30087
30088     if(!this.header){
30089         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30090     }
30091     if(!this.body){
30092         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30093     }
30094
30095     this.header.unselectable();
30096     if(this.title){
30097         this.header.update(this.title);
30098     }
30099     // this element allows the dialog to be focused for keyboard event
30100     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30101     this.focusEl.swallowEvent("click", true);
30102
30103     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30104
30105     // wrap the body and footer for special rendering
30106     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30107     if(this.footer){
30108         this.bwrap.dom.appendChild(this.footer.dom);
30109     }
30110
30111     this.bg = this.el.createChild({
30112         tag: "div", cls:"x-dlg-bg",
30113         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30114     });
30115     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30116
30117
30118     if(this.autoScroll !== false && !this.autoTabs){
30119         this.body.setStyle("overflow", "auto");
30120     }
30121
30122     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30123
30124     if(this.closable !== false){
30125         this.el.addClass("x-dlg-closable");
30126         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30127         this.close.on("click", this.closeClick, this);
30128         this.close.addClassOnOver("x-dlg-close-over");
30129     }
30130     if(this.collapsible !== false){
30131         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30132         this.collapseBtn.on("click", this.collapseClick, this);
30133         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30134         this.header.on("dblclick", this.collapseClick, this);
30135     }
30136     if(this.resizable !== false){
30137         this.el.addClass("x-dlg-resizable");
30138         this.resizer = new Roo.Resizable(el, {
30139             minWidth: this.minWidth || 80,
30140             minHeight:this.minHeight || 80,
30141             handles: this.resizeHandles || "all",
30142             pinned: true
30143         });
30144         this.resizer.on("beforeresize", this.beforeResize, this);
30145         this.resizer.on("resize", this.onResize, this);
30146     }
30147     if(this.draggable !== false){
30148         el.addClass("x-dlg-draggable");
30149         if (!this.proxyDrag) {
30150             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30151         }
30152         else {
30153             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30154         }
30155         dd.setHandleElId(this.header.id);
30156         dd.endDrag = this.endMove.createDelegate(this);
30157         dd.startDrag = this.startMove.createDelegate(this);
30158         dd.onDrag = this.onDrag.createDelegate(this);
30159         dd.scroll = false;
30160         this.dd = dd;
30161     }
30162     if(this.modal){
30163         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30164         this.mask.enableDisplayMode("block");
30165         this.mask.hide();
30166         this.el.addClass("x-dlg-modal");
30167     }
30168     if(this.shadow){
30169         this.shadow = new Roo.Shadow({
30170             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30171             offset : this.shadowOffset
30172         });
30173     }else{
30174         this.shadowOffset = 0;
30175     }
30176     if(Roo.useShims && this.shim !== false){
30177         this.shim = this.el.createShim();
30178         this.shim.hide = this.hideAction;
30179         this.shim.hide();
30180     }else{
30181         this.shim = false;
30182     }
30183     if(this.autoTabs){
30184         this.initTabs();
30185     }
30186     if (this.buttons) { 
30187         var bts= this.buttons;
30188         this.buttons = [];
30189         Roo.each(bts, function(b) {
30190             this.addButton(b);
30191         }, this);
30192     }
30193     
30194     
30195     this.addEvents({
30196         /**
30197          * @event keydown
30198          * Fires when a key is pressed
30199          * @param {Roo.BasicDialog} this
30200          * @param {Roo.EventObject} e
30201          */
30202         "keydown" : true,
30203         /**
30204          * @event move
30205          * Fires when this dialog is moved by the user.
30206          * @param {Roo.BasicDialog} this
30207          * @param {Number} x The new page X
30208          * @param {Number} y The new page Y
30209          */
30210         "move" : true,
30211         /**
30212          * @event resize
30213          * Fires when this dialog is resized by the user.
30214          * @param {Roo.BasicDialog} this
30215          * @param {Number} width The new width
30216          * @param {Number} height The new height
30217          */
30218         "resize" : true,
30219         /**
30220          * @event beforehide
30221          * Fires before this dialog is hidden.
30222          * @param {Roo.BasicDialog} this
30223          */
30224         "beforehide" : true,
30225         /**
30226          * @event hide
30227          * Fires when this dialog is hidden.
30228          * @param {Roo.BasicDialog} this
30229          */
30230         "hide" : true,
30231         /**
30232          * @event beforeshow
30233          * Fires before this dialog is shown.
30234          * @param {Roo.BasicDialog} this
30235          */
30236         "beforeshow" : true,
30237         /**
30238          * @event show
30239          * Fires when this dialog is shown.
30240          * @param {Roo.BasicDialog} this
30241          */
30242         "show" : true
30243     });
30244     el.on("keydown", this.onKeyDown, this);
30245     el.on("mousedown", this.toFront, this);
30246     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30247     this.el.hide();
30248     Roo.DialogManager.register(this);
30249     Roo.BasicDialog.superclass.constructor.call(this);
30250 };
30251
30252 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30253     shadowOffset: Roo.isIE ? 6 : 5,
30254     minHeight: 80,
30255     minWidth: 200,
30256     minButtonWidth: 75,
30257     defaultButton: null,
30258     buttonAlign: "right",
30259     tabTag: 'div',
30260     firstShow: true,
30261
30262     /**
30263      * Sets the dialog title text
30264      * @param {String} text The title text to display
30265      * @return {Roo.BasicDialog} this
30266      */
30267     setTitle : function(text){
30268         this.header.update(text);
30269         return this;
30270     },
30271
30272     // private
30273     closeClick : function(){
30274         this.hide();
30275     },
30276
30277     // private
30278     collapseClick : function(){
30279         this[this.collapsed ? "expand" : "collapse"]();
30280     },
30281
30282     /**
30283      * Collapses the dialog to its minimized state (only the title bar is visible).
30284      * Equivalent to the user clicking the collapse dialog button.
30285      */
30286     collapse : function(){
30287         if(!this.collapsed){
30288             this.collapsed = true;
30289             this.el.addClass("x-dlg-collapsed");
30290             this.restoreHeight = this.el.getHeight();
30291             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30292         }
30293     },
30294
30295     /**
30296      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30297      * clicking the expand dialog button.
30298      */
30299     expand : function(){
30300         if(this.collapsed){
30301             this.collapsed = false;
30302             this.el.removeClass("x-dlg-collapsed");
30303             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30304         }
30305     },
30306
30307     /**
30308      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30309      * @return {Roo.TabPanel} The tabs component
30310      */
30311     initTabs : function(){
30312         var tabs = this.getTabs();
30313         while(tabs.getTab(0)){
30314             tabs.removeTab(0);
30315         }
30316         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30317             var dom = el.dom;
30318             tabs.addTab(Roo.id(dom), dom.title);
30319             dom.title = "";
30320         });
30321         tabs.activate(0);
30322         return tabs;
30323     },
30324
30325     // private
30326     beforeResize : function(){
30327         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30328     },
30329
30330     // private
30331     onResize : function(){
30332         this.refreshSize();
30333         this.syncBodyHeight();
30334         this.adjustAssets();
30335         this.focus();
30336         this.fireEvent("resize", this, this.size.width, this.size.height);
30337     },
30338
30339     // private
30340     onKeyDown : function(e){
30341         if(this.isVisible()){
30342             this.fireEvent("keydown", this, e);
30343         }
30344     },
30345
30346     /**
30347      * Resizes the dialog.
30348      * @param {Number} width
30349      * @param {Number} height
30350      * @return {Roo.BasicDialog} this
30351      */
30352     resizeTo : function(width, height){
30353         this.el.setSize(width, height);
30354         this.size = {width: width, height: height};
30355         this.syncBodyHeight();
30356         if(this.fixedcenter){
30357             this.center();
30358         }
30359         if(this.isVisible()){
30360             this.constrainXY();
30361             this.adjustAssets();
30362         }
30363         this.fireEvent("resize", this, width, height);
30364         return this;
30365     },
30366
30367
30368     /**
30369      * Resizes the dialog to fit the specified content size.
30370      * @param {Number} width
30371      * @param {Number} height
30372      * @return {Roo.BasicDialog} this
30373      */
30374     setContentSize : function(w, h){
30375         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30376         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30377         //if(!this.el.isBorderBox()){
30378             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30379             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30380         //}
30381         if(this.tabs){
30382             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30383             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30384         }
30385         this.resizeTo(w, h);
30386         return this;
30387     },
30388
30389     /**
30390      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30391      * executed in response to a particular key being pressed while the dialog is active.
30392      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30393      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30394      * @param {Function} fn The function to call
30395      * @param {Object} scope (optional) The scope of the function
30396      * @return {Roo.BasicDialog} this
30397      */
30398     addKeyListener : function(key, fn, scope){
30399         var keyCode, shift, ctrl, alt;
30400         if(typeof key == "object" && !(key instanceof Array)){
30401             keyCode = key["key"];
30402             shift = key["shift"];
30403             ctrl = key["ctrl"];
30404             alt = key["alt"];
30405         }else{
30406             keyCode = key;
30407         }
30408         var handler = function(dlg, e){
30409             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30410                 var k = e.getKey();
30411                 if(keyCode instanceof Array){
30412                     for(var i = 0, len = keyCode.length; i < len; i++){
30413                         if(keyCode[i] == k){
30414                           fn.call(scope || window, dlg, k, e);
30415                           return;
30416                         }
30417                     }
30418                 }else{
30419                     if(k == keyCode){
30420                         fn.call(scope || window, dlg, k, e);
30421                     }
30422                 }
30423             }
30424         };
30425         this.on("keydown", handler);
30426         return this;
30427     },
30428
30429     /**
30430      * Returns the TabPanel component (creates it if it doesn't exist).
30431      * Note: If you wish to simply check for the existence of tabs without creating them,
30432      * check for a null 'tabs' property.
30433      * @return {Roo.TabPanel} The tabs component
30434      */
30435     getTabs : function(){
30436         if(!this.tabs){
30437             this.el.addClass("x-dlg-auto-tabs");
30438             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30439             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30440         }
30441         return this.tabs;
30442     },
30443
30444     /**
30445      * Adds a button to the footer section of the dialog.
30446      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30447      * object or a valid Roo.DomHelper element config
30448      * @param {Function} handler The function called when the button is clicked
30449      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30450      * @return {Roo.Button} The new button
30451      */
30452     addButton : function(config, handler, scope){
30453         var dh = Roo.DomHelper;
30454         if(!this.footer){
30455             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30456         }
30457         if(!this.btnContainer){
30458             var tb = this.footer.createChild({
30459
30460                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30461                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30462             }, null, true);
30463             this.btnContainer = tb.firstChild.firstChild.firstChild;
30464         }
30465         var bconfig = {
30466             handler: handler,
30467             scope: scope,
30468             minWidth: this.minButtonWidth,
30469             hideParent:true
30470         };
30471         if(typeof config == "string"){
30472             bconfig.text = config;
30473         }else{
30474             if(config.tag){
30475                 bconfig.dhconfig = config;
30476             }else{
30477                 Roo.apply(bconfig, config);
30478             }
30479         }
30480         var fc = false;
30481         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30482             bconfig.position = Math.max(0, bconfig.position);
30483             fc = this.btnContainer.childNodes[bconfig.position];
30484         }
30485          
30486         var btn = new Roo.Button(
30487             fc ? 
30488                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30489                 : this.btnContainer.appendChild(document.createElement("td")),
30490             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30491             bconfig
30492         );
30493         this.syncBodyHeight();
30494         if(!this.buttons){
30495             /**
30496              * Array of all the buttons that have been added to this dialog via addButton
30497              * @type Array
30498              */
30499             this.buttons = [];
30500         }
30501         this.buttons.push(btn);
30502         return btn;
30503     },
30504
30505     /**
30506      * Sets the default button to be focused when the dialog is displayed.
30507      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30508      * @return {Roo.BasicDialog} this
30509      */
30510     setDefaultButton : function(btn){
30511         this.defaultButton = btn;
30512         return this;
30513     },
30514
30515     // private
30516     getHeaderFooterHeight : function(safe){
30517         var height = 0;
30518         if(this.header){
30519            height += this.header.getHeight();
30520         }
30521         if(this.footer){
30522            var fm = this.footer.getMargins();
30523             height += (this.footer.getHeight()+fm.top+fm.bottom);
30524         }
30525         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30526         height += this.centerBg.getPadding("tb");
30527         return height;
30528     },
30529
30530     // private
30531     syncBodyHeight : function()
30532     {
30533         var bd = this.body, // the text
30534             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30535             bw = this.bwrap;
30536         var height = this.size.height - this.getHeaderFooterHeight(false);
30537         bd.setHeight(height-bd.getMargins("tb"));
30538         var hh = this.header.getHeight();
30539         var h = this.size.height-hh;
30540         cb.setHeight(h);
30541         
30542         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30543         bw.setHeight(h-cb.getPadding("tb"));
30544         
30545         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30546         bd.setWidth(bw.getWidth(true));
30547         if(this.tabs){
30548             this.tabs.syncHeight();
30549             if(Roo.isIE){
30550                 this.tabs.el.repaint();
30551             }
30552         }
30553     },
30554
30555     /**
30556      * Restores the previous state of the dialog if Roo.state is configured.
30557      * @return {Roo.BasicDialog} this
30558      */
30559     restoreState : function(){
30560         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30561         if(box && box.width){
30562             this.xy = [box.x, box.y];
30563             this.resizeTo(box.width, box.height);
30564         }
30565         return this;
30566     },
30567
30568     // private
30569     beforeShow : function(){
30570         this.expand();
30571         if(this.fixedcenter){
30572             this.xy = this.el.getCenterXY(true);
30573         }
30574         if(this.modal){
30575             Roo.get(document.body).addClass("x-body-masked");
30576             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30577             this.mask.show();
30578         }
30579         this.constrainXY();
30580     },
30581
30582     // private
30583     animShow : function(){
30584         var b = Roo.get(this.animateTarget).getBox();
30585         this.proxy.setSize(b.width, b.height);
30586         this.proxy.setLocation(b.x, b.y);
30587         this.proxy.show();
30588         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30589                     true, .35, this.showEl.createDelegate(this));
30590     },
30591
30592     /**
30593      * Shows the dialog.
30594      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30595      * @return {Roo.BasicDialog} this
30596      */
30597     show : function(animateTarget){
30598         if (this.fireEvent("beforeshow", this) === false){
30599             return;
30600         }
30601         if(this.syncHeightBeforeShow){
30602             this.syncBodyHeight();
30603         }else if(this.firstShow){
30604             this.firstShow = false;
30605             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30606         }
30607         this.animateTarget = animateTarget || this.animateTarget;
30608         if(!this.el.isVisible()){
30609             this.beforeShow();
30610             if(this.animateTarget && Roo.get(this.animateTarget)){
30611                 this.animShow();
30612             }else{
30613                 this.showEl();
30614             }
30615         }
30616         return this;
30617     },
30618
30619     // private
30620     showEl : function(){
30621         this.proxy.hide();
30622         this.el.setXY(this.xy);
30623         this.el.show();
30624         this.adjustAssets(true);
30625         this.toFront();
30626         this.focus();
30627         // IE peekaboo bug - fix found by Dave Fenwick
30628         if(Roo.isIE){
30629             this.el.repaint();
30630         }
30631         this.fireEvent("show", this);
30632     },
30633
30634     /**
30635      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30636      * dialog itself will receive focus.
30637      */
30638     focus : function(){
30639         if(this.defaultButton){
30640             this.defaultButton.focus();
30641         }else{
30642             this.focusEl.focus();
30643         }
30644     },
30645
30646     // private
30647     constrainXY : function(){
30648         if(this.constraintoviewport !== false){
30649             if(!this.viewSize){
30650                 if(this.container){
30651                     var s = this.container.getSize();
30652                     this.viewSize = [s.width, s.height];
30653                 }else{
30654                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30655                 }
30656             }
30657             var s = Roo.get(this.container||document).getScroll();
30658
30659             var x = this.xy[0], y = this.xy[1];
30660             var w = this.size.width, h = this.size.height;
30661             var vw = this.viewSize[0], vh = this.viewSize[1];
30662             // only move it if it needs it
30663             var moved = false;
30664             // first validate right/bottom
30665             if(x + w > vw+s.left){
30666                 x = vw - w;
30667                 moved = true;
30668             }
30669             if(y + h > vh+s.top){
30670                 y = vh - h;
30671                 moved = true;
30672             }
30673             // then make sure top/left isn't negative
30674             if(x < s.left){
30675                 x = s.left;
30676                 moved = true;
30677             }
30678             if(y < s.top){
30679                 y = s.top;
30680                 moved = true;
30681             }
30682             if(moved){
30683                 // cache xy
30684                 this.xy = [x, y];
30685                 if(this.isVisible()){
30686                     this.el.setLocation(x, y);
30687                     this.adjustAssets();
30688                 }
30689             }
30690         }
30691     },
30692
30693     // private
30694     onDrag : function(){
30695         if(!this.proxyDrag){
30696             this.xy = this.el.getXY();
30697             this.adjustAssets();
30698         }
30699     },
30700
30701     // private
30702     adjustAssets : function(doShow){
30703         var x = this.xy[0], y = this.xy[1];
30704         var w = this.size.width, h = this.size.height;
30705         if(doShow === true){
30706             if(this.shadow){
30707                 this.shadow.show(this.el);
30708             }
30709             if(this.shim){
30710                 this.shim.show();
30711             }
30712         }
30713         if(this.shadow && this.shadow.isVisible()){
30714             this.shadow.show(this.el);
30715         }
30716         if(this.shim && this.shim.isVisible()){
30717             this.shim.setBounds(x, y, w, h);
30718         }
30719     },
30720
30721     // private
30722     adjustViewport : function(w, h){
30723         if(!w || !h){
30724             w = Roo.lib.Dom.getViewWidth();
30725             h = Roo.lib.Dom.getViewHeight();
30726         }
30727         // cache the size
30728         this.viewSize = [w, h];
30729         if(this.modal && this.mask.isVisible()){
30730             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30731             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30732         }
30733         if(this.isVisible()){
30734             this.constrainXY();
30735         }
30736     },
30737
30738     /**
30739      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30740      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30741      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30742      */
30743     destroy : function(removeEl){
30744         if(this.isVisible()){
30745             this.animateTarget = null;
30746             this.hide();
30747         }
30748         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30749         if(this.tabs){
30750             this.tabs.destroy(removeEl);
30751         }
30752         Roo.destroy(
30753              this.shim,
30754              this.proxy,
30755              this.resizer,
30756              this.close,
30757              this.mask
30758         );
30759         if(this.dd){
30760             this.dd.unreg();
30761         }
30762         if(this.buttons){
30763            for(var i = 0, len = this.buttons.length; i < len; i++){
30764                this.buttons[i].destroy();
30765            }
30766         }
30767         this.el.removeAllListeners();
30768         if(removeEl === true){
30769             this.el.update("");
30770             this.el.remove();
30771         }
30772         Roo.DialogManager.unregister(this);
30773     },
30774
30775     // private
30776     startMove : function(){
30777         if(this.proxyDrag){
30778             this.proxy.show();
30779         }
30780         if(this.constraintoviewport !== false){
30781             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30782         }
30783     },
30784
30785     // private
30786     endMove : function(){
30787         if(!this.proxyDrag){
30788             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30789         }else{
30790             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30791             this.proxy.hide();
30792         }
30793         this.refreshSize();
30794         this.adjustAssets();
30795         this.focus();
30796         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30797     },
30798
30799     /**
30800      * Brings this dialog to the front of any other visible dialogs
30801      * @return {Roo.BasicDialog} this
30802      */
30803     toFront : function(){
30804         Roo.DialogManager.bringToFront(this);
30805         return this;
30806     },
30807
30808     /**
30809      * Sends this dialog to the back (under) of any other visible dialogs
30810      * @return {Roo.BasicDialog} this
30811      */
30812     toBack : function(){
30813         Roo.DialogManager.sendToBack(this);
30814         return this;
30815     },
30816
30817     /**
30818      * Centers this dialog in the viewport
30819      * @return {Roo.BasicDialog} this
30820      */
30821     center : function(){
30822         var xy = this.el.getCenterXY(true);
30823         this.moveTo(xy[0], xy[1]);
30824         return this;
30825     },
30826
30827     /**
30828      * Moves the dialog's top-left corner to the specified point
30829      * @param {Number} x
30830      * @param {Number} y
30831      * @return {Roo.BasicDialog} this
30832      */
30833     moveTo : function(x, y){
30834         this.xy = [x,y];
30835         if(this.isVisible()){
30836             this.el.setXY(this.xy);
30837             this.adjustAssets();
30838         }
30839         return this;
30840     },
30841
30842     /**
30843      * Aligns the dialog to the specified element
30844      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30845      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30846      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30847      * @return {Roo.BasicDialog} this
30848      */
30849     alignTo : function(element, position, offsets){
30850         this.xy = this.el.getAlignToXY(element, position, offsets);
30851         if(this.isVisible()){
30852             this.el.setXY(this.xy);
30853             this.adjustAssets();
30854         }
30855         return this;
30856     },
30857
30858     /**
30859      * Anchors an element to another element and realigns it when the window is resized.
30860      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30861      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30862      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30863      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30864      * is a number, it is used as the buffer delay (defaults to 50ms).
30865      * @return {Roo.BasicDialog} this
30866      */
30867     anchorTo : function(el, alignment, offsets, monitorScroll){
30868         var action = function(){
30869             this.alignTo(el, alignment, offsets);
30870         };
30871         Roo.EventManager.onWindowResize(action, this);
30872         var tm = typeof monitorScroll;
30873         if(tm != 'undefined'){
30874             Roo.EventManager.on(window, 'scroll', action, this,
30875                 {buffer: tm == 'number' ? monitorScroll : 50});
30876         }
30877         action.call(this);
30878         return this;
30879     },
30880
30881     /**
30882      * Returns true if the dialog is visible
30883      * @return {Boolean}
30884      */
30885     isVisible : function(){
30886         return this.el.isVisible();
30887     },
30888
30889     // private
30890     animHide : function(callback){
30891         var b = Roo.get(this.animateTarget).getBox();
30892         this.proxy.show();
30893         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30894         this.el.hide();
30895         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30896                     this.hideEl.createDelegate(this, [callback]));
30897     },
30898
30899     /**
30900      * Hides the dialog.
30901      * @param {Function} callback (optional) Function to call when the dialog is hidden
30902      * @return {Roo.BasicDialog} this
30903      */
30904     hide : function(callback){
30905         if (this.fireEvent("beforehide", this) === false){
30906             return;
30907         }
30908         if(this.shadow){
30909             this.shadow.hide();
30910         }
30911         if(this.shim) {
30912           this.shim.hide();
30913         }
30914         // sometimes animateTarget seems to get set.. causing problems...
30915         // this just double checks..
30916         if(this.animateTarget && Roo.get(this.animateTarget)) {
30917            this.animHide(callback);
30918         }else{
30919             this.el.hide();
30920             this.hideEl(callback);
30921         }
30922         return this;
30923     },
30924
30925     // private
30926     hideEl : function(callback){
30927         this.proxy.hide();
30928         if(this.modal){
30929             this.mask.hide();
30930             Roo.get(document.body).removeClass("x-body-masked");
30931         }
30932         this.fireEvent("hide", this);
30933         if(typeof callback == "function"){
30934             callback();
30935         }
30936     },
30937
30938     // private
30939     hideAction : function(){
30940         this.setLeft("-10000px");
30941         this.setTop("-10000px");
30942         this.setStyle("visibility", "hidden");
30943     },
30944
30945     // private
30946     refreshSize : function(){
30947         this.size = this.el.getSize();
30948         this.xy = this.el.getXY();
30949         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30950     },
30951
30952     // private
30953     // z-index is managed by the DialogManager and may be overwritten at any time
30954     setZIndex : function(index){
30955         if(this.modal){
30956             this.mask.setStyle("z-index", index);
30957         }
30958         if(this.shim){
30959             this.shim.setStyle("z-index", ++index);
30960         }
30961         if(this.shadow){
30962             this.shadow.setZIndex(++index);
30963         }
30964         this.el.setStyle("z-index", ++index);
30965         if(this.proxy){
30966             this.proxy.setStyle("z-index", ++index);
30967         }
30968         if(this.resizer){
30969             this.resizer.proxy.setStyle("z-index", ++index);
30970         }
30971
30972         this.lastZIndex = index;
30973     },
30974
30975     /**
30976      * Returns the element for this dialog
30977      * @return {Roo.Element} The underlying dialog Element
30978      */
30979     getEl : function(){
30980         return this.el;
30981     }
30982 });
30983
30984 /**
30985  * @class Roo.DialogManager
30986  * Provides global access to BasicDialogs that have been created and
30987  * support for z-indexing (layering) multiple open dialogs.
30988  */
30989 Roo.DialogManager = function(){
30990     var list = {};
30991     var accessList = [];
30992     var front = null;
30993
30994     // private
30995     var sortDialogs = function(d1, d2){
30996         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30997     };
30998
30999     // private
31000     var orderDialogs = function(){
31001         accessList.sort(sortDialogs);
31002         var seed = Roo.DialogManager.zseed;
31003         for(var i = 0, len = accessList.length; i < len; i++){
31004             var dlg = accessList[i];
31005             if(dlg){
31006                 dlg.setZIndex(seed + (i*10));
31007             }
31008         }
31009     };
31010
31011     return {
31012         /**
31013          * The starting z-index for BasicDialogs (defaults to 9000)
31014          * @type Number The z-index value
31015          */
31016         zseed : 9000,
31017
31018         // private
31019         register : function(dlg){
31020             list[dlg.id] = dlg;
31021             accessList.push(dlg);
31022         },
31023
31024         // private
31025         unregister : function(dlg){
31026             delete list[dlg.id];
31027             var i=0;
31028             var len=0;
31029             if(!accessList.indexOf){
31030                 for(  i = 0, len = accessList.length; i < len; i++){
31031                     if(accessList[i] == dlg){
31032                         accessList.splice(i, 1);
31033                         return;
31034                     }
31035                 }
31036             }else{
31037                  i = accessList.indexOf(dlg);
31038                 if(i != -1){
31039                     accessList.splice(i, 1);
31040                 }
31041             }
31042         },
31043
31044         /**
31045          * Gets a registered dialog by id
31046          * @param {String/Object} id The id of the dialog or a dialog
31047          * @return {Roo.BasicDialog} this
31048          */
31049         get : function(id){
31050             return typeof id == "object" ? id : list[id];
31051         },
31052
31053         /**
31054          * Brings the specified dialog to the front
31055          * @param {String/Object} dlg The id of the dialog or a dialog
31056          * @return {Roo.BasicDialog} this
31057          */
31058         bringToFront : function(dlg){
31059             dlg = this.get(dlg);
31060             if(dlg != front){
31061                 front = dlg;
31062                 dlg._lastAccess = new Date().getTime();
31063                 orderDialogs();
31064             }
31065             return dlg;
31066         },
31067
31068         /**
31069          * Sends the specified dialog to the back
31070          * @param {String/Object} dlg The id of the dialog or a dialog
31071          * @return {Roo.BasicDialog} this
31072          */
31073         sendToBack : function(dlg){
31074             dlg = this.get(dlg);
31075             dlg._lastAccess = -(new Date().getTime());
31076             orderDialogs();
31077             return dlg;
31078         },
31079
31080         /**
31081          * Hides all dialogs
31082          */
31083         hideAll : function(){
31084             for(var id in list){
31085                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31086                     list[id].hide();
31087                 }
31088             }
31089         }
31090     };
31091 }();
31092
31093 /**
31094  * @class Roo.LayoutDialog
31095  * @extends Roo.BasicDialog
31096  * Dialog which provides adjustments for working with a layout in a Dialog.
31097  * Add your necessary layout config options to the dialog's config.<br>
31098  * Example usage (including a nested layout):
31099  * <pre><code>
31100 if(!dialog){
31101     dialog = new Roo.LayoutDialog("download-dlg", {
31102         modal: true,
31103         width:600,
31104         height:450,
31105         shadow:true,
31106         minWidth:500,
31107         minHeight:350,
31108         autoTabs:true,
31109         proxyDrag:true,
31110         // layout config merges with the dialog config
31111         center:{
31112             tabPosition: "top",
31113             alwaysShowTabs: true
31114         }
31115     });
31116     dialog.addKeyListener(27, dialog.hide, dialog);
31117     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31118     dialog.addButton("Build It!", this.getDownload, this);
31119
31120     // we can even add nested layouts
31121     var innerLayout = new Roo.BorderLayout("dl-inner", {
31122         east: {
31123             initialSize: 200,
31124             autoScroll:true,
31125             split:true
31126         },
31127         center: {
31128             autoScroll:true
31129         }
31130     });
31131     innerLayout.beginUpdate();
31132     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31133     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31134     innerLayout.endUpdate(true);
31135
31136     var layout = dialog.getLayout();
31137     layout.beginUpdate();
31138     layout.add("center", new Roo.ContentPanel("standard-panel",
31139                         {title: "Download the Source", fitToFrame:true}));
31140     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31141                {title: "Build your own roo.js"}));
31142     layout.getRegion("center").showPanel(sp);
31143     layout.endUpdate();
31144 }
31145 </code></pre>
31146     * @constructor
31147     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31148     * @param {Object} config configuration options
31149   */
31150 Roo.LayoutDialog = function(el, cfg){
31151     
31152     var config=  cfg;
31153     if (typeof(cfg) == 'undefined') {
31154         config = Roo.apply({}, el);
31155         // not sure why we use documentElement here.. - it should always be body.
31156         // IE7 borks horribly if we use documentElement.
31157         // webkit also does not like documentElement - it creates a body element...
31158         el = Roo.get( document.body || document.documentElement ).createChild();
31159         //config.autoCreate = true;
31160     }
31161     
31162     
31163     config.autoTabs = false;
31164     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31165     this.body.setStyle({overflow:"hidden", position:"relative"});
31166     this.layout = new Roo.BorderLayout(this.body.dom, config);
31167     this.layout.monitorWindowResize = false;
31168     this.el.addClass("x-dlg-auto-layout");
31169     // fix case when center region overwrites center function
31170     this.center = Roo.BasicDialog.prototype.center;
31171     this.on("show", this.layout.layout, this.layout, true);
31172     if (config.items) {
31173         var xitems = config.items;
31174         delete config.items;
31175         Roo.each(xitems, this.addxtype, this);
31176     }
31177     
31178     
31179 };
31180 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31181     /**
31182      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31183      * @deprecated
31184      */
31185     endUpdate : function(){
31186         this.layout.endUpdate();
31187     },
31188
31189     /**
31190      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31191      *  @deprecated
31192      */
31193     beginUpdate : function(){
31194         this.layout.beginUpdate();
31195     },
31196
31197     /**
31198      * Get the BorderLayout for this dialog
31199      * @return {Roo.BorderLayout}
31200      */
31201     getLayout : function(){
31202         return this.layout;
31203     },
31204
31205     showEl : function(){
31206         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31207         if(Roo.isIE7){
31208             this.layout.layout();
31209         }
31210     },
31211
31212     // private
31213     // Use the syncHeightBeforeShow config option to control this automatically
31214     syncBodyHeight : function(){
31215         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31216         if(this.layout){this.layout.layout();}
31217     },
31218     
31219       /**
31220      * Add an xtype element (actually adds to the layout.)
31221      * @return {Object} xdata xtype object data.
31222      */
31223     
31224     addxtype : function(c) {
31225         return this.layout.addxtype(c);
31226     }
31227 });/*
31228  * Based on:
31229  * Ext JS Library 1.1.1
31230  * Copyright(c) 2006-2007, Ext JS, LLC.
31231  *
31232  * Originally Released Under LGPL - original licence link has changed is not relivant.
31233  *
31234  * Fork - LGPL
31235  * <script type="text/javascript">
31236  */
31237  
31238 /**
31239  * @class Roo.MessageBox
31240  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31241  * Example usage:
31242  *<pre><code>
31243 // Basic alert:
31244 Roo.Msg.alert('Status', 'Changes saved successfully.');
31245
31246 // Prompt for user data:
31247 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31248     if (btn == 'ok'){
31249         // process text value...
31250     }
31251 });
31252
31253 // Show a dialog using config options:
31254 Roo.Msg.show({
31255    title:'Save Changes?',
31256    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31257    buttons: Roo.Msg.YESNOCANCEL,
31258    fn: processResult,
31259    animEl: 'elId'
31260 });
31261 </code></pre>
31262  * @singleton
31263  */
31264 Roo.MessageBox = function(){
31265     var dlg, opt, mask, waitTimer;
31266     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31267     var buttons, activeTextEl, bwidth;
31268
31269     // private
31270     var handleButton = function(button){
31271         dlg.hide();
31272         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31273     };
31274
31275     // private
31276     var handleHide = function(){
31277         if(opt && opt.cls){
31278             dlg.el.removeClass(opt.cls);
31279         }
31280         if(waitTimer){
31281             Roo.TaskMgr.stop(waitTimer);
31282             waitTimer = null;
31283         }
31284     };
31285
31286     // private
31287     var updateButtons = function(b){
31288         var width = 0;
31289         if(!b){
31290             buttons["ok"].hide();
31291             buttons["cancel"].hide();
31292             buttons["yes"].hide();
31293             buttons["no"].hide();
31294             dlg.footer.dom.style.display = 'none';
31295             return width;
31296         }
31297         dlg.footer.dom.style.display = '';
31298         for(var k in buttons){
31299             if(typeof buttons[k] != "function"){
31300                 if(b[k]){
31301                     buttons[k].show();
31302                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31303                     width += buttons[k].el.getWidth()+15;
31304                 }else{
31305                     buttons[k].hide();
31306                 }
31307             }
31308         }
31309         return width;
31310     };
31311
31312     // private
31313     var handleEsc = function(d, k, e){
31314         if(opt && opt.closable !== false){
31315             dlg.hide();
31316         }
31317         if(e){
31318             e.stopEvent();
31319         }
31320     };
31321
31322     return {
31323         /**
31324          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31325          * @return {Roo.BasicDialog} The BasicDialog element
31326          */
31327         getDialog : function(){
31328            if(!dlg){
31329                 dlg = new Roo.BasicDialog("x-msg-box", {
31330                     autoCreate : true,
31331                     shadow: true,
31332                     draggable: true,
31333                     resizable:false,
31334                     constraintoviewport:false,
31335                     fixedcenter:true,
31336                     collapsible : false,
31337                     shim:true,
31338                     modal: true,
31339                     width:400, height:100,
31340                     buttonAlign:"center",
31341                     closeClick : function(){
31342                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31343                             handleButton("no");
31344                         }else{
31345                             handleButton("cancel");
31346                         }
31347                     }
31348                 });
31349                 dlg.on("hide", handleHide);
31350                 mask = dlg.mask;
31351                 dlg.addKeyListener(27, handleEsc);
31352                 buttons = {};
31353                 var bt = this.buttonText;
31354                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31355                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31356                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31357                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31358                 bodyEl = dlg.body.createChild({
31359
31360                     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>'
31361                 });
31362                 msgEl = bodyEl.dom.firstChild;
31363                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31364                 textboxEl.enableDisplayMode();
31365                 textboxEl.addKeyListener([10,13], function(){
31366                     if(dlg.isVisible() && opt && opt.buttons){
31367                         if(opt.buttons.ok){
31368                             handleButton("ok");
31369                         }else if(opt.buttons.yes){
31370                             handleButton("yes");
31371                         }
31372                     }
31373                 });
31374                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31375                 textareaEl.enableDisplayMode();
31376                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31377                 progressEl.enableDisplayMode();
31378                 var pf = progressEl.dom.firstChild;
31379                 if (pf) {
31380                     pp = Roo.get(pf.firstChild);
31381                     pp.setHeight(pf.offsetHeight);
31382                 }
31383                 
31384             }
31385             return dlg;
31386         },
31387
31388         /**
31389          * Updates the message box body text
31390          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31391          * the XHTML-compliant non-breaking space character '&amp;#160;')
31392          * @return {Roo.MessageBox} This message box
31393          */
31394         updateText : function(text){
31395             if(!dlg.isVisible() && !opt.width){
31396                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31397             }
31398             msgEl.innerHTML = text || '&#160;';
31399       
31400             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31401             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31402             var w = Math.max(
31403                     Math.min(opt.width || cw , this.maxWidth), 
31404                     Math.max(opt.minWidth || this.minWidth, bwidth)
31405             );
31406             if(opt.prompt){
31407                 activeTextEl.setWidth(w);
31408             }
31409             if(dlg.isVisible()){
31410                 dlg.fixedcenter = false;
31411             }
31412             // to big, make it scroll. = But as usual stupid IE does not support
31413             // !important..
31414             
31415             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31416                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31417                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31418             } else {
31419                 bodyEl.dom.style.height = '';
31420                 bodyEl.dom.style.overflowY = '';
31421             }
31422             if (cw > w) {
31423                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31424             } else {
31425                 bodyEl.dom.style.overflowX = '';
31426             }
31427             
31428             dlg.setContentSize(w, bodyEl.getHeight());
31429             if(dlg.isVisible()){
31430                 dlg.fixedcenter = true;
31431             }
31432             return this;
31433         },
31434
31435         /**
31436          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31437          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31438          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31439          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31440          * @return {Roo.MessageBox} This message box
31441          */
31442         updateProgress : function(value, text){
31443             if(text){
31444                 this.updateText(text);
31445             }
31446             if (pp) { // weird bug on my firefox - for some reason this is not defined
31447                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31448             }
31449             return this;
31450         },        
31451
31452         /**
31453          * Returns true if the message box is currently displayed
31454          * @return {Boolean} True if the message box is visible, else false
31455          */
31456         isVisible : function(){
31457             return dlg && dlg.isVisible();  
31458         },
31459
31460         /**
31461          * Hides the message box if it is displayed
31462          */
31463         hide : function(){
31464             if(this.isVisible()){
31465                 dlg.hide();
31466             }  
31467         },
31468
31469         /**
31470          * Displays a new message box, or reinitializes an existing message box, based on the config options
31471          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31472          * The following config object properties are supported:
31473          * <pre>
31474 Property    Type             Description
31475 ----------  ---------------  ------------------------------------------------------------------------------------
31476 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31477                                    closes (defaults to undefined)
31478 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31479                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31480 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31481                                    progress and wait dialogs will ignore this property and always hide the
31482                                    close button as they can only be closed programmatically.
31483 cls               String           A custom CSS class to apply to the message box element
31484 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31485                                    displayed (defaults to 75)
31486 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31487                                    function will be btn (the name of the button that was clicked, if applicable,
31488                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31489                                    Progress and wait dialogs will ignore this option since they do not respond to
31490                                    user actions and can only be closed programmatically, so any required function
31491                                    should be called by the same code after it closes the dialog.
31492 icon              String           A CSS class that provides a background image to be used as an icon for
31493                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31494 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31495 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31496 modal             Boolean          False to allow user interaction with the page while the message box is
31497                                    displayed (defaults to true)
31498 msg               String           A string that will replace the existing message box body text (defaults
31499                                    to the XHTML-compliant non-breaking space character '&#160;')
31500 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31501 progress          Boolean          True to display a progress bar (defaults to false)
31502 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31503 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31504 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31505 title             String           The title text
31506 value             String           The string value to set into the active textbox element if displayed
31507 wait              Boolean          True to display a progress bar (defaults to false)
31508 width             Number           The width of the dialog in pixels
31509 </pre>
31510          *
31511          * Example usage:
31512          * <pre><code>
31513 Roo.Msg.show({
31514    title: 'Address',
31515    msg: 'Please enter your address:',
31516    width: 300,
31517    buttons: Roo.MessageBox.OKCANCEL,
31518    multiline: true,
31519    fn: saveAddress,
31520    animEl: 'addAddressBtn'
31521 });
31522 </code></pre>
31523          * @param {Object} config Configuration options
31524          * @return {Roo.MessageBox} This message box
31525          */
31526         show : function(options)
31527         {
31528             
31529             // this causes nightmares if you show one dialog after another
31530             // especially on callbacks..
31531              
31532             if(this.isVisible()){
31533                 
31534                 this.hide();
31535                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31536                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31537                 Roo.log("New Dialog Message:" +  options.msg )
31538                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31539                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31540                 
31541             }
31542             var d = this.getDialog();
31543             opt = options;
31544             d.setTitle(opt.title || "&#160;");
31545             d.close.setDisplayed(opt.closable !== false);
31546             activeTextEl = textboxEl;
31547             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31548             if(opt.prompt){
31549                 if(opt.multiline){
31550                     textboxEl.hide();
31551                     textareaEl.show();
31552                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31553                         opt.multiline : this.defaultTextHeight);
31554                     activeTextEl = textareaEl;
31555                 }else{
31556                     textboxEl.show();
31557                     textareaEl.hide();
31558                 }
31559             }else{
31560                 textboxEl.hide();
31561                 textareaEl.hide();
31562             }
31563             progressEl.setDisplayed(opt.progress === true);
31564             this.updateProgress(0);
31565             activeTextEl.dom.value = opt.value || "";
31566             if(opt.prompt){
31567                 dlg.setDefaultButton(activeTextEl);
31568             }else{
31569                 var bs = opt.buttons;
31570                 var db = null;
31571                 if(bs && bs.ok){
31572                     db = buttons["ok"];
31573                 }else if(bs && bs.yes){
31574                     db = buttons["yes"];
31575                 }
31576                 dlg.setDefaultButton(db);
31577             }
31578             bwidth = updateButtons(opt.buttons);
31579             this.updateText(opt.msg);
31580             if(opt.cls){
31581                 d.el.addClass(opt.cls);
31582             }
31583             d.proxyDrag = opt.proxyDrag === true;
31584             d.modal = opt.modal !== false;
31585             d.mask = opt.modal !== false ? mask : false;
31586             if(!d.isVisible()){
31587                 // force it to the end of the z-index stack so it gets a cursor in FF
31588                 document.body.appendChild(dlg.el.dom);
31589                 d.animateTarget = null;
31590                 d.show(options.animEl);
31591             }
31592             return this;
31593         },
31594
31595         /**
31596          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31597          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31598          * and closing the message box when the process is complete.
31599          * @param {String} title The title bar text
31600          * @param {String} msg The message box body text
31601          * @return {Roo.MessageBox} This message box
31602          */
31603         progress : function(title, msg){
31604             this.show({
31605                 title : title,
31606                 msg : msg,
31607                 buttons: false,
31608                 progress:true,
31609                 closable:false,
31610                 minWidth: this.minProgressWidth,
31611                 modal : true
31612             });
31613             return this;
31614         },
31615
31616         /**
31617          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31618          * If a callback function is passed it will be called after the user clicks the button, and the
31619          * id of the button that was clicked will be passed as the only parameter to the callback
31620          * (could also be the top-right close button).
31621          * @param {String} title The title bar text
31622          * @param {String} msg The message box body text
31623          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31624          * @param {Object} scope (optional) The scope of the callback function
31625          * @return {Roo.MessageBox} This message box
31626          */
31627         alert : function(title, msg, fn, scope){
31628             this.show({
31629                 title : title,
31630                 msg : msg,
31631                 buttons: this.OK,
31632                 fn: fn,
31633                 scope : scope,
31634                 modal : true
31635             });
31636             return this;
31637         },
31638
31639         /**
31640          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31641          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31642          * You are responsible for closing the message box when the process is complete.
31643          * @param {String} msg The message box body text
31644          * @param {String} title (optional) The title bar text
31645          * @return {Roo.MessageBox} This message box
31646          */
31647         wait : function(msg, title){
31648             this.show({
31649                 title : title,
31650                 msg : msg,
31651                 buttons: false,
31652                 closable:false,
31653                 progress:true,
31654                 modal:true,
31655                 width:300,
31656                 wait:true
31657             });
31658             waitTimer = Roo.TaskMgr.start({
31659                 run: function(i){
31660                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31661                 },
31662                 interval: 1000
31663             });
31664             return this;
31665         },
31666
31667         /**
31668          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31669          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31670          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31671          * @param {String} title The title bar text
31672          * @param {String} msg The message box body text
31673          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31674          * @param {Object} scope (optional) The scope of the callback function
31675          * @return {Roo.MessageBox} This message box
31676          */
31677         confirm : function(title, msg, fn, scope){
31678             this.show({
31679                 title : title,
31680                 msg : msg,
31681                 buttons: this.YESNO,
31682                 fn: fn,
31683                 scope : scope,
31684                 modal : true
31685             });
31686             return this;
31687         },
31688
31689         /**
31690          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31691          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31692          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31693          * (could also be the top-right close button) and the text that was entered will be passed as the two
31694          * parameters to the callback.
31695          * @param {String} title The title bar text
31696          * @param {String} msg The message box body text
31697          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31698          * @param {Object} scope (optional) The scope of the callback function
31699          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31700          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31701          * @return {Roo.MessageBox} This message box
31702          */
31703         prompt : function(title, msg, fn, scope, multiline){
31704             this.show({
31705                 title : title,
31706                 msg : msg,
31707                 buttons: this.OKCANCEL,
31708                 fn: fn,
31709                 minWidth:250,
31710                 scope : scope,
31711                 prompt:true,
31712                 multiline: multiline,
31713                 modal : true
31714             });
31715             return this;
31716         },
31717
31718         /**
31719          * Button config that displays a single OK button
31720          * @type Object
31721          */
31722         OK : {ok:true},
31723         /**
31724          * Button config that displays Yes and No buttons
31725          * @type Object
31726          */
31727         YESNO : {yes:true, no:true},
31728         /**
31729          * Button config that displays OK and Cancel buttons
31730          * @type Object
31731          */
31732         OKCANCEL : {ok:true, cancel:true},
31733         /**
31734          * Button config that displays Yes, No and Cancel buttons
31735          * @type Object
31736          */
31737         YESNOCANCEL : {yes:true, no:true, cancel:true},
31738
31739         /**
31740          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31741          * @type Number
31742          */
31743         defaultTextHeight : 75,
31744         /**
31745          * The maximum width in pixels of the message box (defaults to 600)
31746          * @type Number
31747          */
31748         maxWidth : 600,
31749         /**
31750          * The minimum width in pixels of the message box (defaults to 100)
31751          * @type Number
31752          */
31753         minWidth : 100,
31754         /**
31755          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31756          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31757          * @type Number
31758          */
31759         minProgressWidth : 250,
31760         /**
31761          * An object containing the default button text strings that can be overriden for localized language support.
31762          * Supported properties are: ok, cancel, yes and no.
31763          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31764          * @type Object
31765          */
31766         buttonText : {
31767             ok : "OK",
31768             cancel : "Cancel",
31769             yes : "Yes",
31770             no : "No"
31771         }
31772     };
31773 }();
31774
31775 /**
31776  * Shorthand for {@link Roo.MessageBox}
31777  */
31778 Roo.Msg = Roo.MessageBox;/*
31779  * Based on:
31780  * Ext JS Library 1.1.1
31781  * Copyright(c) 2006-2007, Ext JS, LLC.
31782  *
31783  * Originally Released Under LGPL - original licence link has changed is not relivant.
31784  *
31785  * Fork - LGPL
31786  * <script type="text/javascript">
31787  */
31788 /**
31789  * @class Roo.QuickTips
31790  * Provides attractive and customizable tooltips for any element.
31791  * @singleton
31792  */
31793 Roo.QuickTips = function(){
31794     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31795     var ce, bd, xy, dd;
31796     var visible = false, disabled = true, inited = false;
31797     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31798     
31799     var onOver = function(e){
31800         if(disabled){
31801             return;
31802         }
31803         var t = e.getTarget();
31804         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31805             return;
31806         }
31807         if(ce && t == ce.el){
31808             clearTimeout(hideProc);
31809             return;
31810         }
31811         if(t && tagEls[t.id]){
31812             tagEls[t.id].el = t;
31813             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31814             return;
31815         }
31816         var ttp, et = Roo.fly(t);
31817         var ns = cfg.namespace;
31818         if(tm.interceptTitles && t.title){
31819             ttp = t.title;
31820             t.qtip = ttp;
31821             t.removeAttribute("title");
31822             e.preventDefault();
31823         }else{
31824             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31825         }
31826         if(ttp){
31827             showProc = show.defer(tm.showDelay, tm, [{
31828                 el: t, 
31829                 text: ttp, 
31830                 width: et.getAttributeNS(ns, cfg.width),
31831                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31832                 title: et.getAttributeNS(ns, cfg.title),
31833                     cls: et.getAttributeNS(ns, cfg.cls)
31834             }]);
31835         }
31836     };
31837     
31838     var onOut = function(e){
31839         clearTimeout(showProc);
31840         var t = e.getTarget();
31841         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31842             hideProc = setTimeout(hide, tm.hideDelay);
31843         }
31844     };
31845     
31846     var onMove = function(e){
31847         if(disabled){
31848             return;
31849         }
31850         xy = e.getXY();
31851         xy[1] += 18;
31852         if(tm.trackMouse && ce){
31853             el.setXY(xy);
31854         }
31855     };
31856     
31857     var onDown = function(e){
31858         clearTimeout(showProc);
31859         clearTimeout(hideProc);
31860         if(!e.within(el)){
31861             if(tm.hideOnClick){
31862                 hide();
31863                 tm.disable();
31864                 tm.enable.defer(100, tm);
31865             }
31866         }
31867     };
31868     
31869     var getPad = function(){
31870         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31871     };
31872
31873     var show = function(o){
31874         if(disabled){
31875             return;
31876         }
31877         clearTimeout(dismissProc);
31878         ce = o;
31879         if(removeCls){ // in case manually hidden
31880             el.removeClass(removeCls);
31881             removeCls = null;
31882         }
31883         if(ce.cls){
31884             el.addClass(ce.cls);
31885             removeCls = ce.cls;
31886         }
31887         if(ce.title){
31888             tipTitle.update(ce.title);
31889             tipTitle.show();
31890         }else{
31891             tipTitle.update('');
31892             tipTitle.hide();
31893         }
31894         el.dom.style.width  = tm.maxWidth+'px';
31895         //tipBody.dom.style.width = '';
31896         tipBodyText.update(o.text);
31897         var p = getPad(), w = ce.width;
31898         if(!w){
31899             var td = tipBodyText.dom;
31900             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31901             if(aw > tm.maxWidth){
31902                 w = tm.maxWidth;
31903             }else if(aw < tm.minWidth){
31904                 w = tm.minWidth;
31905             }else{
31906                 w = aw;
31907             }
31908         }
31909         //tipBody.setWidth(w);
31910         el.setWidth(parseInt(w, 10) + p);
31911         if(ce.autoHide === false){
31912             close.setDisplayed(true);
31913             if(dd){
31914                 dd.unlock();
31915             }
31916         }else{
31917             close.setDisplayed(false);
31918             if(dd){
31919                 dd.lock();
31920             }
31921         }
31922         if(xy){
31923             el.avoidY = xy[1]-18;
31924             el.setXY(xy);
31925         }
31926         if(tm.animate){
31927             el.setOpacity(.1);
31928             el.setStyle("visibility", "visible");
31929             el.fadeIn({callback: afterShow});
31930         }else{
31931             afterShow();
31932         }
31933     };
31934     
31935     var afterShow = function(){
31936         if(ce){
31937             el.show();
31938             esc.enable();
31939             if(tm.autoDismiss && ce.autoHide !== false){
31940                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31941             }
31942         }
31943     };
31944     
31945     var hide = function(noanim){
31946         clearTimeout(dismissProc);
31947         clearTimeout(hideProc);
31948         ce = null;
31949         if(el.isVisible()){
31950             esc.disable();
31951             if(noanim !== true && tm.animate){
31952                 el.fadeOut({callback: afterHide});
31953             }else{
31954                 afterHide();
31955             } 
31956         }
31957     };
31958     
31959     var afterHide = function(){
31960         el.hide();
31961         if(removeCls){
31962             el.removeClass(removeCls);
31963             removeCls = null;
31964         }
31965     };
31966     
31967     return {
31968         /**
31969         * @cfg {Number} minWidth
31970         * The minimum width of the quick tip (defaults to 40)
31971         */
31972        minWidth : 40,
31973         /**
31974         * @cfg {Number} maxWidth
31975         * The maximum width of the quick tip (defaults to 300)
31976         */
31977        maxWidth : 300,
31978         /**
31979         * @cfg {Boolean} interceptTitles
31980         * True to automatically use the element's DOM title value if available (defaults to false)
31981         */
31982        interceptTitles : false,
31983         /**
31984         * @cfg {Boolean} trackMouse
31985         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31986         */
31987        trackMouse : false,
31988         /**
31989         * @cfg {Boolean} hideOnClick
31990         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31991         */
31992        hideOnClick : true,
31993         /**
31994         * @cfg {Number} showDelay
31995         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31996         */
31997        showDelay : 500,
31998         /**
31999         * @cfg {Number} hideDelay
32000         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32001         */
32002        hideDelay : 200,
32003         /**
32004         * @cfg {Boolean} autoHide
32005         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32006         * Used in conjunction with hideDelay.
32007         */
32008        autoHide : true,
32009         /**
32010         * @cfg {Boolean}
32011         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32012         * (defaults to true).  Used in conjunction with autoDismissDelay.
32013         */
32014        autoDismiss : true,
32015         /**
32016         * @cfg {Number}
32017         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32018         */
32019        autoDismissDelay : 5000,
32020        /**
32021         * @cfg {Boolean} animate
32022         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32023         */
32024        animate : false,
32025
32026        /**
32027         * @cfg {String} title
32028         * Title text to display (defaults to '').  This can be any valid HTML markup.
32029         */
32030         title: '',
32031        /**
32032         * @cfg {String} text
32033         * Body text to display (defaults to '').  This can be any valid HTML markup.
32034         */
32035         text : '',
32036        /**
32037         * @cfg {String} cls
32038         * A CSS class to apply to the base quick tip element (defaults to '').
32039         */
32040         cls : '',
32041        /**
32042         * @cfg {Number} width
32043         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32044         * minWidth or maxWidth.
32045         */
32046         width : null,
32047
32048     /**
32049      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32050      * or display QuickTips in a page.
32051      */
32052        init : function(){
32053           tm = Roo.QuickTips;
32054           cfg = tm.tagConfig;
32055           if(!inited){
32056               if(!Roo.isReady){ // allow calling of init() before onReady
32057                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32058                   return;
32059               }
32060               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32061               el.fxDefaults = {stopFx: true};
32062               // maximum custom styling
32063               //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>');
32064               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>');              
32065               tipTitle = el.child('h3');
32066               tipTitle.enableDisplayMode("block");
32067               tipBody = el.child('div.x-tip-bd');
32068               tipBodyText = el.child('div.x-tip-bd-inner');
32069               //bdLeft = el.child('div.x-tip-bd-left');
32070               //bdRight = el.child('div.x-tip-bd-right');
32071               close = el.child('div.x-tip-close');
32072               close.enableDisplayMode("block");
32073               close.on("click", hide);
32074               var d = Roo.get(document);
32075               d.on("mousedown", onDown);
32076               d.on("mouseover", onOver);
32077               d.on("mouseout", onOut);
32078               d.on("mousemove", onMove);
32079               esc = d.addKeyListener(27, hide);
32080               esc.disable();
32081               if(Roo.dd.DD){
32082                   dd = el.initDD("default", null, {
32083                       onDrag : function(){
32084                           el.sync();  
32085                       }
32086                   });
32087                   dd.setHandleElId(tipTitle.id);
32088                   dd.lock();
32089               }
32090               inited = true;
32091           }
32092           this.enable(); 
32093        },
32094
32095     /**
32096      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32097      * are supported:
32098      * <pre>
32099 Property    Type                   Description
32100 ----------  ---------------------  ------------------------------------------------------------------------
32101 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32102      * </ul>
32103      * @param {Object} config The config object
32104      */
32105        register : function(config){
32106            var cs = config instanceof Array ? config : arguments;
32107            for(var i = 0, len = cs.length; i < len; i++) {
32108                var c = cs[i];
32109                var target = c.target;
32110                if(target){
32111                    if(target instanceof Array){
32112                        for(var j = 0, jlen = target.length; j < jlen; j++){
32113                            tagEls[target[j]] = c;
32114                        }
32115                    }else{
32116                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32117                    }
32118                }
32119            }
32120        },
32121
32122     /**
32123      * Removes this quick tip from its element and destroys it.
32124      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32125      */
32126        unregister : function(el){
32127            delete tagEls[Roo.id(el)];
32128        },
32129
32130     /**
32131      * Enable this quick tip.
32132      */
32133        enable : function(){
32134            if(inited && disabled){
32135                locks.pop();
32136                if(locks.length < 1){
32137                    disabled = false;
32138                }
32139            }
32140        },
32141
32142     /**
32143      * Disable this quick tip.
32144      */
32145        disable : function(){
32146           disabled = true;
32147           clearTimeout(showProc);
32148           clearTimeout(hideProc);
32149           clearTimeout(dismissProc);
32150           if(ce){
32151               hide(true);
32152           }
32153           locks.push(1);
32154        },
32155
32156     /**
32157      * Returns true if the quick tip is enabled, else false.
32158      */
32159        isEnabled : function(){
32160             return !disabled;
32161        },
32162
32163         // private
32164        tagConfig : {
32165            namespace : "ext",
32166            attribute : "qtip",
32167            width : "width",
32168            target : "target",
32169            title : "qtitle",
32170            hide : "hide",
32171            cls : "qclass"
32172        }
32173    };
32174 }();
32175
32176 // backwards compat
32177 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32178  * Based on:
32179  * Ext JS Library 1.1.1
32180  * Copyright(c) 2006-2007, Ext JS, LLC.
32181  *
32182  * Originally Released Under LGPL - original licence link has changed is not relivant.
32183  *
32184  * Fork - LGPL
32185  * <script type="text/javascript">
32186  */
32187  
32188
32189 /**
32190  * @class Roo.tree.TreePanel
32191  * @extends Roo.data.Tree
32192
32193  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32194  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32195  * @cfg {Boolean} enableDD true to enable drag and drop
32196  * @cfg {Boolean} enableDrag true to enable just drag
32197  * @cfg {Boolean} enableDrop true to enable just drop
32198  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32199  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32200  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32201  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32202  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32203  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32204  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32205  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32206  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32207  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32208  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32209  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32210  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32211  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32212  * @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>
32213  * @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>
32214  * 
32215  * @constructor
32216  * @param {String/HTMLElement/Element} el The container element
32217  * @param {Object} config
32218  */
32219 Roo.tree.TreePanel = function(el, config){
32220     var root = false;
32221     var loader = false;
32222     if (config.root) {
32223         root = config.root;
32224         delete config.root;
32225     }
32226     if (config.loader) {
32227         loader = config.loader;
32228         delete config.loader;
32229     }
32230     
32231     Roo.apply(this, config);
32232     Roo.tree.TreePanel.superclass.constructor.call(this);
32233     this.el = Roo.get(el);
32234     this.el.addClass('x-tree');
32235     //console.log(root);
32236     if (root) {
32237         this.setRootNode( Roo.factory(root, Roo.tree));
32238     }
32239     if (loader) {
32240         this.loader = Roo.factory(loader, Roo.tree);
32241     }
32242    /**
32243     * Read-only. The id of the container element becomes this TreePanel's id.
32244     */
32245     this.id = this.el.id;
32246     this.addEvents({
32247         /**
32248         * @event beforeload
32249         * Fires before a node is loaded, return false to cancel
32250         * @param {Node} node The node being loaded
32251         */
32252         "beforeload" : true,
32253         /**
32254         * @event load
32255         * Fires when a node is loaded
32256         * @param {Node} node The node that was loaded
32257         */
32258         "load" : true,
32259         /**
32260         * @event textchange
32261         * Fires when the text for a node is changed
32262         * @param {Node} node The node
32263         * @param {String} text The new text
32264         * @param {String} oldText The old text
32265         */
32266         "textchange" : true,
32267         /**
32268         * @event beforeexpand
32269         * Fires before a node is expanded, return false to cancel.
32270         * @param {Node} node The node
32271         * @param {Boolean} deep
32272         * @param {Boolean} anim
32273         */
32274         "beforeexpand" : true,
32275         /**
32276         * @event beforecollapse
32277         * Fires before a node is collapsed, return false to cancel.
32278         * @param {Node} node The node
32279         * @param {Boolean} deep
32280         * @param {Boolean} anim
32281         */
32282         "beforecollapse" : true,
32283         /**
32284         * @event expand
32285         * Fires when a node is expanded
32286         * @param {Node} node The node
32287         */
32288         "expand" : true,
32289         /**
32290         * @event disabledchange
32291         * Fires when the disabled status of a node changes
32292         * @param {Node} node The node
32293         * @param {Boolean} disabled
32294         */
32295         "disabledchange" : true,
32296         /**
32297         * @event collapse
32298         * Fires when a node is collapsed
32299         * @param {Node} node The node
32300         */
32301         "collapse" : true,
32302         /**
32303         * @event beforeclick
32304         * Fires before click processing on a node. Return false to cancel the default action.
32305         * @param {Node} node The node
32306         * @param {Roo.EventObject} e The event object
32307         */
32308         "beforeclick":true,
32309         /**
32310         * @event checkchange
32311         * Fires when a node with a checkbox's checked property changes
32312         * @param {Node} this This node
32313         * @param {Boolean} checked
32314         */
32315         "checkchange":true,
32316         /**
32317         * @event click
32318         * Fires when a node is clicked
32319         * @param {Node} node The node
32320         * @param {Roo.EventObject} e The event object
32321         */
32322         "click":true,
32323         /**
32324         * @event dblclick
32325         * Fires when a node is double clicked
32326         * @param {Node} node The node
32327         * @param {Roo.EventObject} e The event object
32328         */
32329         "dblclick":true,
32330         /**
32331         * @event contextmenu
32332         * Fires when a node is right clicked
32333         * @param {Node} node The node
32334         * @param {Roo.EventObject} e The event object
32335         */
32336         "contextmenu":true,
32337         /**
32338         * @event beforechildrenrendered
32339         * Fires right before the child nodes for a node are rendered
32340         * @param {Node} node The node
32341         */
32342         "beforechildrenrendered":true,
32343         /**
32344         * @event startdrag
32345         * Fires when a node starts being dragged
32346         * @param {Roo.tree.TreePanel} this
32347         * @param {Roo.tree.TreeNode} node
32348         * @param {event} e The raw browser event
32349         */ 
32350        "startdrag" : true,
32351        /**
32352         * @event enddrag
32353         * Fires when a drag operation is complete
32354         * @param {Roo.tree.TreePanel} this
32355         * @param {Roo.tree.TreeNode} node
32356         * @param {event} e The raw browser event
32357         */
32358        "enddrag" : true,
32359        /**
32360         * @event dragdrop
32361         * Fires when a dragged node is dropped on a valid DD target
32362         * @param {Roo.tree.TreePanel} this
32363         * @param {Roo.tree.TreeNode} node
32364         * @param {DD} dd The dd it was dropped on
32365         * @param {event} e The raw browser event
32366         */
32367        "dragdrop" : true,
32368        /**
32369         * @event beforenodedrop
32370         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32371         * passed to handlers has the following properties:<br />
32372         * <ul style="padding:5px;padding-left:16px;">
32373         * <li>tree - The TreePanel</li>
32374         * <li>target - The node being targeted for the drop</li>
32375         * <li>data - The drag data from the drag source</li>
32376         * <li>point - The point of the drop - append, above or below</li>
32377         * <li>source - The drag source</li>
32378         * <li>rawEvent - Raw mouse event</li>
32379         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32380         * to be inserted by setting them on this object.</li>
32381         * <li>cancel - Set this to true to cancel the drop.</li>
32382         * </ul>
32383         * @param {Object} dropEvent
32384         */
32385        "beforenodedrop" : true,
32386        /**
32387         * @event nodedrop
32388         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32389         * passed to handlers has the following properties:<br />
32390         * <ul style="padding:5px;padding-left:16px;">
32391         * <li>tree - The TreePanel</li>
32392         * <li>target - The node being targeted for the drop</li>
32393         * <li>data - The drag data from the drag source</li>
32394         * <li>point - The point of the drop - append, above or below</li>
32395         * <li>source - The drag source</li>
32396         * <li>rawEvent - Raw mouse event</li>
32397         * <li>dropNode - Dropped node(s).</li>
32398         * </ul>
32399         * @param {Object} dropEvent
32400         */
32401        "nodedrop" : true,
32402         /**
32403         * @event nodedragover
32404         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32405         * passed to handlers has the following properties:<br />
32406         * <ul style="padding:5px;padding-left:16px;">
32407         * <li>tree - The TreePanel</li>
32408         * <li>target - The node being targeted for the drop</li>
32409         * <li>data - The drag data from the drag source</li>
32410         * <li>point - The point of the drop - append, above or below</li>
32411         * <li>source - The drag source</li>
32412         * <li>rawEvent - Raw mouse event</li>
32413         * <li>dropNode - Drop node(s) provided by the source.</li>
32414         * <li>cancel - Set this to true to signal drop not allowed.</li>
32415         * </ul>
32416         * @param {Object} dragOverEvent
32417         */
32418        "nodedragover" : true
32419         
32420     });
32421     if(this.singleExpand){
32422        this.on("beforeexpand", this.restrictExpand, this);
32423     }
32424     if (this.editor) {
32425         this.editor.tree = this;
32426         this.editor = Roo.factory(this.editor, Roo.tree);
32427     }
32428     
32429     if (this.selModel) {
32430         this.selModel = Roo.factory(this.selModel, Roo.tree);
32431     }
32432    
32433 };
32434 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32435     rootVisible : true,
32436     animate: Roo.enableFx,
32437     lines : true,
32438     enableDD : false,
32439     hlDrop : Roo.enableFx,
32440   
32441     renderer: false,
32442     
32443     rendererTip: false,
32444     // private
32445     restrictExpand : function(node){
32446         var p = node.parentNode;
32447         if(p){
32448             if(p.expandedChild && p.expandedChild.parentNode == p){
32449                 p.expandedChild.collapse();
32450             }
32451             p.expandedChild = node;
32452         }
32453     },
32454
32455     // private override
32456     setRootNode : function(node){
32457         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32458         if(!this.rootVisible){
32459             node.ui = new Roo.tree.RootTreeNodeUI(node);
32460         }
32461         return node;
32462     },
32463
32464     /**
32465      * Returns the container element for this TreePanel
32466      */
32467     getEl : function(){
32468         return this.el;
32469     },
32470
32471     /**
32472      * Returns the default TreeLoader for this TreePanel
32473      */
32474     getLoader : function(){
32475         return this.loader;
32476     },
32477
32478     /**
32479      * Expand all nodes
32480      */
32481     expandAll : function(){
32482         this.root.expand(true);
32483     },
32484
32485     /**
32486      * Collapse all nodes
32487      */
32488     collapseAll : function(){
32489         this.root.collapse(true);
32490     },
32491
32492     /**
32493      * Returns the selection model used by this TreePanel
32494      */
32495     getSelectionModel : function(){
32496         if(!this.selModel){
32497             this.selModel = new Roo.tree.DefaultSelectionModel();
32498         }
32499         return this.selModel;
32500     },
32501
32502     /**
32503      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32504      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32505      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32506      * @return {Array}
32507      */
32508     getChecked : function(a, startNode){
32509         startNode = startNode || this.root;
32510         var r = [];
32511         var f = function(){
32512             if(this.attributes.checked){
32513                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32514             }
32515         }
32516         startNode.cascade(f);
32517         return r;
32518     },
32519
32520     /**
32521      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32522      * @param {String} path
32523      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32524      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32525      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32526      */
32527     expandPath : function(path, attr, callback){
32528         attr = attr || "id";
32529         var keys = path.split(this.pathSeparator);
32530         var curNode = this.root;
32531         if(curNode.attributes[attr] != keys[1]){ // invalid root
32532             if(callback){
32533                 callback(false, null);
32534             }
32535             return;
32536         }
32537         var index = 1;
32538         var f = function(){
32539             if(++index == keys.length){
32540                 if(callback){
32541                     callback(true, curNode);
32542                 }
32543                 return;
32544             }
32545             var c = curNode.findChild(attr, keys[index]);
32546             if(!c){
32547                 if(callback){
32548                     callback(false, curNode);
32549                 }
32550                 return;
32551             }
32552             curNode = c;
32553             c.expand(false, false, f);
32554         };
32555         curNode.expand(false, false, f);
32556     },
32557
32558     /**
32559      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32560      * @param {String} path
32561      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32562      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32563      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32564      */
32565     selectPath : function(path, attr, callback){
32566         attr = attr || "id";
32567         var keys = path.split(this.pathSeparator);
32568         var v = keys.pop();
32569         if(keys.length > 0){
32570             var f = function(success, node){
32571                 if(success && node){
32572                     var n = node.findChild(attr, v);
32573                     if(n){
32574                         n.select();
32575                         if(callback){
32576                             callback(true, n);
32577                         }
32578                     }else if(callback){
32579                         callback(false, n);
32580                     }
32581                 }else{
32582                     if(callback){
32583                         callback(false, n);
32584                     }
32585                 }
32586             };
32587             this.expandPath(keys.join(this.pathSeparator), attr, f);
32588         }else{
32589             this.root.select();
32590             if(callback){
32591                 callback(true, this.root);
32592             }
32593         }
32594     },
32595
32596     getTreeEl : function(){
32597         return this.el;
32598     },
32599
32600     /**
32601      * Trigger rendering of this TreePanel
32602      */
32603     render : function(){
32604         if (this.innerCt) {
32605             return this; // stop it rendering more than once!!
32606         }
32607         
32608         this.innerCt = this.el.createChild({tag:"ul",
32609                cls:"x-tree-root-ct " +
32610                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32611
32612         if(this.containerScroll){
32613             Roo.dd.ScrollManager.register(this.el);
32614         }
32615         if((this.enableDD || this.enableDrop) && !this.dropZone){
32616            /**
32617             * The dropZone used by this tree if drop is enabled
32618             * @type Roo.tree.TreeDropZone
32619             */
32620              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32621                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32622            });
32623         }
32624         if((this.enableDD || this.enableDrag) && !this.dragZone){
32625            /**
32626             * The dragZone used by this tree if drag is enabled
32627             * @type Roo.tree.TreeDragZone
32628             */
32629             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32630                ddGroup: this.ddGroup || "TreeDD",
32631                scroll: this.ddScroll
32632            });
32633         }
32634         this.getSelectionModel().init(this);
32635         if (!this.root) {
32636             Roo.log("ROOT not set in tree");
32637             return this;
32638         }
32639         this.root.render();
32640         if(!this.rootVisible){
32641             this.root.renderChildren();
32642         }
32643         return this;
32644     }
32645 });/*
32646  * Based on:
32647  * Ext JS Library 1.1.1
32648  * Copyright(c) 2006-2007, Ext JS, LLC.
32649  *
32650  * Originally Released Under LGPL - original licence link has changed is not relivant.
32651  *
32652  * Fork - LGPL
32653  * <script type="text/javascript">
32654  */
32655  
32656
32657 /**
32658  * @class Roo.tree.DefaultSelectionModel
32659  * @extends Roo.util.Observable
32660  * The default single selection for a TreePanel.
32661  * @param {Object} cfg Configuration
32662  */
32663 Roo.tree.DefaultSelectionModel = function(cfg){
32664    this.selNode = null;
32665    
32666    
32667    
32668    this.addEvents({
32669        /**
32670         * @event selectionchange
32671         * Fires when the selected node changes
32672         * @param {DefaultSelectionModel} this
32673         * @param {TreeNode} node the new selection
32674         */
32675        "selectionchange" : true,
32676
32677        /**
32678         * @event beforeselect
32679         * Fires before the selected node changes, return false to cancel the change
32680         * @param {DefaultSelectionModel} this
32681         * @param {TreeNode} node the new selection
32682         * @param {TreeNode} node the old selection
32683         */
32684        "beforeselect" : true
32685    });
32686    
32687     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32688 };
32689
32690 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32691     init : function(tree){
32692         this.tree = tree;
32693         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32694         tree.on("click", this.onNodeClick, this);
32695     },
32696     
32697     onNodeClick : function(node, e){
32698         if (e.ctrlKey && this.selNode == node)  {
32699             this.unselect(node);
32700             return;
32701         }
32702         this.select(node);
32703     },
32704     
32705     /**
32706      * Select a node.
32707      * @param {TreeNode} node The node to select
32708      * @return {TreeNode} The selected node
32709      */
32710     select : function(node){
32711         var last = this.selNode;
32712         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32713             if(last){
32714                 last.ui.onSelectedChange(false);
32715             }
32716             this.selNode = node;
32717             node.ui.onSelectedChange(true);
32718             this.fireEvent("selectionchange", this, node, last);
32719         }
32720         return node;
32721     },
32722     
32723     /**
32724      * Deselect a node.
32725      * @param {TreeNode} node The node to unselect
32726      */
32727     unselect : function(node){
32728         if(this.selNode == node){
32729             this.clearSelections();
32730         }    
32731     },
32732     
32733     /**
32734      * Clear all selections
32735      */
32736     clearSelections : function(){
32737         var n = this.selNode;
32738         if(n){
32739             n.ui.onSelectedChange(false);
32740             this.selNode = null;
32741             this.fireEvent("selectionchange", this, null);
32742         }
32743         return n;
32744     },
32745     
32746     /**
32747      * Get the selected node
32748      * @return {TreeNode} The selected node
32749      */
32750     getSelectedNode : function(){
32751         return this.selNode;    
32752     },
32753     
32754     /**
32755      * Returns true if the node is selected
32756      * @param {TreeNode} node The node to check
32757      * @return {Boolean}
32758      */
32759     isSelected : function(node){
32760         return this.selNode == node;  
32761     },
32762
32763     /**
32764      * Selects the node above the selected node in the tree, intelligently walking the nodes
32765      * @return TreeNode The new selection
32766      */
32767     selectPrevious : function(){
32768         var s = this.selNode || this.lastSelNode;
32769         if(!s){
32770             return null;
32771         }
32772         var ps = s.previousSibling;
32773         if(ps){
32774             if(!ps.isExpanded() || ps.childNodes.length < 1){
32775                 return this.select(ps);
32776             } else{
32777                 var lc = ps.lastChild;
32778                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32779                     lc = lc.lastChild;
32780                 }
32781                 return this.select(lc);
32782             }
32783         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32784             return this.select(s.parentNode);
32785         }
32786         return null;
32787     },
32788
32789     /**
32790      * Selects the node above the selected node in the tree, intelligently walking the nodes
32791      * @return TreeNode The new selection
32792      */
32793     selectNext : function(){
32794         var s = this.selNode || this.lastSelNode;
32795         if(!s){
32796             return null;
32797         }
32798         if(s.firstChild && s.isExpanded()){
32799              return this.select(s.firstChild);
32800          }else if(s.nextSibling){
32801              return this.select(s.nextSibling);
32802          }else if(s.parentNode){
32803             var newS = null;
32804             s.parentNode.bubble(function(){
32805                 if(this.nextSibling){
32806                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32807                     return false;
32808                 }
32809             });
32810             return newS;
32811          }
32812         return null;
32813     },
32814
32815     onKeyDown : function(e){
32816         var s = this.selNode || this.lastSelNode;
32817         // undesirable, but required
32818         var sm = this;
32819         if(!s){
32820             return;
32821         }
32822         var k = e.getKey();
32823         switch(k){
32824              case e.DOWN:
32825                  e.stopEvent();
32826                  this.selectNext();
32827              break;
32828              case e.UP:
32829                  e.stopEvent();
32830                  this.selectPrevious();
32831              break;
32832              case e.RIGHT:
32833                  e.preventDefault();
32834                  if(s.hasChildNodes()){
32835                      if(!s.isExpanded()){
32836                          s.expand();
32837                      }else if(s.firstChild){
32838                          this.select(s.firstChild, e);
32839                      }
32840                  }
32841              break;
32842              case e.LEFT:
32843                  e.preventDefault();
32844                  if(s.hasChildNodes() && s.isExpanded()){
32845                      s.collapse();
32846                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32847                      this.select(s.parentNode, e);
32848                  }
32849              break;
32850         };
32851     }
32852 });
32853
32854 /**
32855  * @class Roo.tree.MultiSelectionModel
32856  * @extends Roo.util.Observable
32857  * Multi selection for a TreePanel.
32858  * @param {Object} cfg Configuration
32859  */
32860 Roo.tree.MultiSelectionModel = function(){
32861    this.selNodes = [];
32862    this.selMap = {};
32863    this.addEvents({
32864        /**
32865         * @event selectionchange
32866         * Fires when the selected nodes change
32867         * @param {MultiSelectionModel} this
32868         * @param {Array} nodes Array of the selected nodes
32869         */
32870        "selectionchange" : true
32871    });
32872    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32873    
32874 };
32875
32876 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32877     init : function(tree){
32878         this.tree = tree;
32879         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32880         tree.on("click", this.onNodeClick, this);
32881     },
32882     
32883     onNodeClick : function(node, e){
32884         this.select(node, e, e.ctrlKey);
32885     },
32886     
32887     /**
32888      * Select a node.
32889      * @param {TreeNode} node The node to select
32890      * @param {EventObject} e (optional) An event associated with the selection
32891      * @param {Boolean} keepExisting True to retain existing selections
32892      * @return {TreeNode} The selected node
32893      */
32894     select : function(node, e, keepExisting){
32895         if(keepExisting !== true){
32896             this.clearSelections(true);
32897         }
32898         if(this.isSelected(node)){
32899             this.lastSelNode = node;
32900             return node;
32901         }
32902         this.selNodes.push(node);
32903         this.selMap[node.id] = node;
32904         this.lastSelNode = node;
32905         node.ui.onSelectedChange(true);
32906         this.fireEvent("selectionchange", this, this.selNodes);
32907         return node;
32908     },
32909     
32910     /**
32911      * Deselect a node.
32912      * @param {TreeNode} node The node to unselect
32913      */
32914     unselect : function(node){
32915         if(this.selMap[node.id]){
32916             node.ui.onSelectedChange(false);
32917             var sn = this.selNodes;
32918             var index = -1;
32919             if(sn.indexOf){
32920                 index = sn.indexOf(node);
32921             }else{
32922                 for(var i = 0, len = sn.length; i < len; i++){
32923                     if(sn[i] == node){
32924                         index = i;
32925                         break;
32926                     }
32927                 }
32928             }
32929             if(index != -1){
32930                 this.selNodes.splice(index, 1);
32931             }
32932             delete this.selMap[node.id];
32933             this.fireEvent("selectionchange", this, this.selNodes);
32934         }
32935     },
32936     
32937     /**
32938      * Clear all selections
32939      */
32940     clearSelections : function(suppressEvent){
32941         var sn = this.selNodes;
32942         if(sn.length > 0){
32943             for(var i = 0, len = sn.length; i < len; i++){
32944                 sn[i].ui.onSelectedChange(false);
32945             }
32946             this.selNodes = [];
32947             this.selMap = {};
32948             if(suppressEvent !== true){
32949                 this.fireEvent("selectionchange", this, this.selNodes);
32950             }
32951         }
32952     },
32953     
32954     /**
32955      * Returns true if the node is selected
32956      * @param {TreeNode} node The node to check
32957      * @return {Boolean}
32958      */
32959     isSelected : function(node){
32960         return this.selMap[node.id] ? true : false;  
32961     },
32962     
32963     /**
32964      * Returns an array of the selected nodes
32965      * @return {Array}
32966      */
32967     getSelectedNodes : function(){
32968         return this.selNodes;    
32969     },
32970
32971     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32972
32973     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32974
32975     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32976 });/*
32977  * Based on:
32978  * Ext JS Library 1.1.1
32979  * Copyright(c) 2006-2007, Ext JS, LLC.
32980  *
32981  * Originally Released Under LGPL - original licence link has changed is not relivant.
32982  *
32983  * Fork - LGPL
32984  * <script type="text/javascript">
32985  */
32986  
32987 /**
32988  * @class Roo.tree.TreeNode
32989  * @extends Roo.data.Node
32990  * @cfg {String} text The text for this node
32991  * @cfg {Boolean} expanded true to start the node expanded
32992  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32993  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32994  * @cfg {Boolean} disabled true to start the node disabled
32995  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32996  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32997  * @cfg {String} cls A css class to be added to the node
32998  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32999  * @cfg {String} href URL of the link used for the node (defaults to #)
33000  * @cfg {String} hrefTarget target frame for the link
33001  * @cfg {String} qtip An Ext QuickTip for the node
33002  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33003  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33004  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33005  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33006  * (defaults to undefined with no checkbox rendered)
33007  * @constructor
33008  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33009  */
33010 Roo.tree.TreeNode = function(attributes){
33011     attributes = attributes || {};
33012     if(typeof attributes == "string"){
33013         attributes = {text: attributes};
33014     }
33015     this.childrenRendered = false;
33016     this.rendered = false;
33017     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33018     this.expanded = attributes.expanded === true;
33019     this.isTarget = attributes.isTarget !== false;
33020     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33021     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33022
33023     /**
33024      * Read-only. The text for this node. To change it use setText().
33025      * @type String
33026      */
33027     this.text = attributes.text;
33028     /**
33029      * True if this node is disabled.
33030      * @type Boolean
33031      */
33032     this.disabled = attributes.disabled === true;
33033
33034     this.addEvents({
33035         /**
33036         * @event textchange
33037         * Fires when the text for this node is changed
33038         * @param {Node} this This node
33039         * @param {String} text The new text
33040         * @param {String} oldText The old text
33041         */
33042         "textchange" : true,
33043         /**
33044         * @event beforeexpand
33045         * Fires before this node is expanded, return false to cancel.
33046         * @param {Node} this This node
33047         * @param {Boolean} deep
33048         * @param {Boolean} anim
33049         */
33050         "beforeexpand" : true,
33051         /**
33052         * @event beforecollapse
33053         * Fires before this node is collapsed, return false to cancel.
33054         * @param {Node} this This node
33055         * @param {Boolean} deep
33056         * @param {Boolean} anim
33057         */
33058         "beforecollapse" : true,
33059         /**
33060         * @event expand
33061         * Fires when this node is expanded
33062         * @param {Node} this This node
33063         */
33064         "expand" : true,
33065         /**
33066         * @event disabledchange
33067         * Fires when the disabled status of this node changes
33068         * @param {Node} this This node
33069         * @param {Boolean} disabled
33070         */
33071         "disabledchange" : true,
33072         /**
33073         * @event collapse
33074         * Fires when this node is collapsed
33075         * @param {Node} this This node
33076         */
33077         "collapse" : true,
33078         /**
33079         * @event beforeclick
33080         * Fires before click processing. Return false to cancel the default action.
33081         * @param {Node} this This node
33082         * @param {Roo.EventObject} e The event object
33083         */
33084         "beforeclick":true,
33085         /**
33086         * @event checkchange
33087         * Fires when a node with a checkbox's checked property changes
33088         * @param {Node} this This node
33089         * @param {Boolean} checked
33090         */
33091         "checkchange":true,
33092         /**
33093         * @event click
33094         * Fires when this node is clicked
33095         * @param {Node} this This node
33096         * @param {Roo.EventObject} e The event object
33097         */
33098         "click":true,
33099         /**
33100         * @event dblclick
33101         * Fires when this node is double clicked
33102         * @param {Node} this This node
33103         * @param {Roo.EventObject} e The event object
33104         */
33105         "dblclick":true,
33106         /**
33107         * @event contextmenu
33108         * Fires when this node is right clicked
33109         * @param {Node} this This node
33110         * @param {Roo.EventObject} e The event object
33111         */
33112         "contextmenu":true,
33113         /**
33114         * @event beforechildrenrendered
33115         * Fires right before the child nodes for this node are rendered
33116         * @param {Node} this This node
33117         */
33118         "beforechildrenrendered":true
33119     });
33120
33121     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33122
33123     /**
33124      * Read-only. The UI for this node
33125      * @type TreeNodeUI
33126      */
33127     this.ui = new uiClass(this);
33128     
33129     // finally support items[]
33130     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33131         return;
33132     }
33133     
33134     
33135     Roo.each(this.attributes.items, function(c) {
33136         this.appendChild(Roo.factory(c,Roo.Tree));
33137     }, this);
33138     delete this.attributes.items;
33139     
33140     
33141     
33142 };
33143 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33144     preventHScroll: true,
33145     /**
33146      * Returns true if this node is expanded
33147      * @return {Boolean}
33148      */
33149     isExpanded : function(){
33150         return this.expanded;
33151     },
33152
33153     /**
33154      * Returns the UI object for this node
33155      * @return {TreeNodeUI}
33156      */
33157     getUI : function(){
33158         return this.ui;
33159     },
33160
33161     // private override
33162     setFirstChild : function(node){
33163         var of = this.firstChild;
33164         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33165         if(this.childrenRendered && of && node != of){
33166             of.renderIndent(true, true);
33167         }
33168         if(this.rendered){
33169             this.renderIndent(true, true);
33170         }
33171     },
33172
33173     // private override
33174     setLastChild : function(node){
33175         var ol = this.lastChild;
33176         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33177         if(this.childrenRendered && ol && node != ol){
33178             ol.renderIndent(true, true);
33179         }
33180         if(this.rendered){
33181             this.renderIndent(true, true);
33182         }
33183     },
33184
33185     // these methods are overridden to provide lazy rendering support
33186     // private override
33187     appendChild : function()
33188     {
33189         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33190         if(node && this.childrenRendered){
33191             node.render();
33192         }
33193         this.ui.updateExpandIcon();
33194         return node;
33195     },
33196
33197     // private override
33198     removeChild : function(node){
33199         this.ownerTree.getSelectionModel().unselect(node);
33200         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33201         // if it's been rendered remove dom node
33202         if(this.childrenRendered){
33203             node.ui.remove();
33204         }
33205         if(this.childNodes.length < 1){
33206             this.collapse(false, false);
33207         }else{
33208             this.ui.updateExpandIcon();
33209         }
33210         if(!this.firstChild) {
33211             this.childrenRendered = false;
33212         }
33213         return node;
33214     },
33215
33216     // private override
33217     insertBefore : function(node, refNode){
33218         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33219         if(newNode && refNode && this.childrenRendered){
33220             node.render();
33221         }
33222         this.ui.updateExpandIcon();
33223         return newNode;
33224     },
33225
33226     /**
33227      * Sets the text for this node
33228      * @param {String} text
33229      */
33230     setText : function(text){
33231         var oldText = this.text;
33232         this.text = text;
33233         this.attributes.text = text;
33234         if(this.rendered){ // event without subscribing
33235             this.ui.onTextChange(this, text, oldText);
33236         }
33237         this.fireEvent("textchange", this, text, oldText);
33238     },
33239
33240     /**
33241      * Triggers selection of this node
33242      */
33243     select : function(){
33244         this.getOwnerTree().getSelectionModel().select(this);
33245     },
33246
33247     /**
33248      * Triggers deselection of this node
33249      */
33250     unselect : function(){
33251         this.getOwnerTree().getSelectionModel().unselect(this);
33252     },
33253
33254     /**
33255      * Returns true if this node is selected
33256      * @return {Boolean}
33257      */
33258     isSelected : function(){
33259         return this.getOwnerTree().getSelectionModel().isSelected(this);
33260     },
33261
33262     /**
33263      * Expand this node.
33264      * @param {Boolean} deep (optional) True to expand all children as well
33265      * @param {Boolean} anim (optional) false to cancel the default animation
33266      * @param {Function} callback (optional) A callback to be called when
33267      * expanding this node completes (does not wait for deep expand to complete).
33268      * Called with 1 parameter, this node.
33269      */
33270     expand : function(deep, anim, callback){
33271         if(!this.expanded){
33272             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33273                 return;
33274             }
33275             if(!this.childrenRendered){
33276                 this.renderChildren();
33277             }
33278             this.expanded = true;
33279             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33280                 this.ui.animExpand(function(){
33281                     this.fireEvent("expand", this);
33282                     if(typeof callback == "function"){
33283                         callback(this);
33284                     }
33285                     if(deep === true){
33286                         this.expandChildNodes(true);
33287                     }
33288                 }.createDelegate(this));
33289                 return;
33290             }else{
33291                 this.ui.expand();
33292                 this.fireEvent("expand", this);
33293                 if(typeof callback == "function"){
33294                     callback(this);
33295                 }
33296             }
33297         }else{
33298            if(typeof callback == "function"){
33299                callback(this);
33300            }
33301         }
33302         if(deep === true){
33303             this.expandChildNodes(true);
33304         }
33305     },
33306
33307     isHiddenRoot : function(){
33308         return this.isRoot && !this.getOwnerTree().rootVisible;
33309     },
33310
33311     /**
33312      * Collapse this node.
33313      * @param {Boolean} deep (optional) True to collapse all children as well
33314      * @param {Boolean} anim (optional) false to cancel the default animation
33315      */
33316     collapse : function(deep, anim){
33317         if(this.expanded && !this.isHiddenRoot()){
33318             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33319                 return;
33320             }
33321             this.expanded = false;
33322             if((this.getOwnerTree().animate && anim !== false) || anim){
33323                 this.ui.animCollapse(function(){
33324                     this.fireEvent("collapse", this);
33325                     if(deep === true){
33326                         this.collapseChildNodes(true);
33327                     }
33328                 }.createDelegate(this));
33329                 return;
33330             }else{
33331                 this.ui.collapse();
33332                 this.fireEvent("collapse", this);
33333             }
33334         }
33335         if(deep === true){
33336             var cs = this.childNodes;
33337             for(var i = 0, len = cs.length; i < len; i++) {
33338                 cs[i].collapse(true, false);
33339             }
33340         }
33341     },
33342
33343     // private
33344     delayedExpand : function(delay){
33345         if(!this.expandProcId){
33346             this.expandProcId = this.expand.defer(delay, this);
33347         }
33348     },
33349
33350     // private
33351     cancelExpand : function(){
33352         if(this.expandProcId){
33353             clearTimeout(this.expandProcId);
33354         }
33355         this.expandProcId = false;
33356     },
33357
33358     /**
33359      * Toggles expanded/collapsed state of the node
33360      */
33361     toggle : function(){
33362         if(this.expanded){
33363             this.collapse();
33364         }else{
33365             this.expand();
33366         }
33367     },
33368
33369     /**
33370      * Ensures all parent nodes are expanded
33371      */
33372     ensureVisible : function(callback){
33373         var tree = this.getOwnerTree();
33374         tree.expandPath(this.parentNode.getPath(), false, function(){
33375             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33376             Roo.callback(callback);
33377         }.createDelegate(this));
33378     },
33379
33380     /**
33381      * Expand all child nodes
33382      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33383      */
33384     expandChildNodes : function(deep){
33385         var cs = this.childNodes;
33386         for(var i = 0, len = cs.length; i < len; i++) {
33387                 cs[i].expand(deep);
33388         }
33389     },
33390
33391     /**
33392      * Collapse all child nodes
33393      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33394      */
33395     collapseChildNodes : function(deep){
33396         var cs = this.childNodes;
33397         for(var i = 0, len = cs.length; i < len; i++) {
33398                 cs[i].collapse(deep);
33399         }
33400     },
33401
33402     /**
33403      * Disables this node
33404      */
33405     disable : function(){
33406         this.disabled = true;
33407         this.unselect();
33408         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33409             this.ui.onDisableChange(this, true);
33410         }
33411         this.fireEvent("disabledchange", this, true);
33412     },
33413
33414     /**
33415      * Enables this node
33416      */
33417     enable : function(){
33418         this.disabled = false;
33419         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33420             this.ui.onDisableChange(this, false);
33421         }
33422         this.fireEvent("disabledchange", this, false);
33423     },
33424
33425     // private
33426     renderChildren : function(suppressEvent){
33427         if(suppressEvent !== false){
33428             this.fireEvent("beforechildrenrendered", this);
33429         }
33430         var cs = this.childNodes;
33431         for(var i = 0, len = cs.length; i < len; i++){
33432             cs[i].render(true);
33433         }
33434         this.childrenRendered = true;
33435     },
33436
33437     // private
33438     sort : function(fn, scope){
33439         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33440         if(this.childrenRendered){
33441             var cs = this.childNodes;
33442             for(var i = 0, len = cs.length; i < len; i++){
33443                 cs[i].render(true);
33444             }
33445         }
33446     },
33447
33448     // private
33449     render : function(bulkRender){
33450         this.ui.render(bulkRender);
33451         if(!this.rendered){
33452             this.rendered = true;
33453             if(this.expanded){
33454                 this.expanded = false;
33455                 this.expand(false, false);
33456             }
33457         }
33458     },
33459
33460     // private
33461     renderIndent : function(deep, refresh){
33462         if(refresh){
33463             this.ui.childIndent = null;
33464         }
33465         this.ui.renderIndent();
33466         if(deep === true && this.childrenRendered){
33467             var cs = this.childNodes;
33468             for(var i = 0, len = cs.length; i < len; i++){
33469                 cs[i].renderIndent(true, refresh);
33470             }
33471         }
33472     }
33473 });/*
33474  * Based on:
33475  * Ext JS Library 1.1.1
33476  * Copyright(c) 2006-2007, Ext JS, LLC.
33477  *
33478  * Originally Released Under LGPL - original licence link has changed is not relivant.
33479  *
33480  * Fork - LGPL
33481  * <script type="text/javascript">
33482  */
33483  
33484 /**
33485  * @class Roo.tree.AsyncTreeNode
33486  * @extends Roo.tree.TreeNode
33487  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33488  * @constructor
33489  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33490  */
33491  Roo.tree.AsyncTreeNode = function(config){
33492     this.loaded = false;
33493     this.loading = false;
33494     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33495     /**
33496     * @event beforeload
33497     * Fires before this node is loaded, return false to cancel
33498     * @param {Node} this This node
33499     */
33500     this.addEvents({'beforeload':true, 'load': true});
33501     /**
33502     * @event load
33503     * Fires when this node is loaded
33504     * @param {Node} this This node
33505     */
33506     /**
33507      * The loader used by this node (defaults to using the tree's defined loader)
33508      * @type TreeLoader
33509      * @property loader
33510      */
33511 };
33512 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33513     expand : function(deep, anim, callback){
33514         if(this.loading){ // if an async load is already running, waiting til it's done
33515             var timer;
33516             var f = function(){
33517                 if(!this.loading){ // done loading
33518                     clearInterval(timer);
33519                     this.expand(deep, anim, callback);
33520                 }
33521             }.createDelegate(this);
33522             timer = setInterval(f, 200);
33523             return;
33524         }
33525         if(!this.loaded){
33526             if(this.fireEvent("beforeload", this) === false){
33527                 return;
33528             }
33529             this.loading = true;
33530             this.ui.beforeLoad(this);
33531             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33532             if(loader){
33533                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33534                 return;
33535             }
33536         }
33537         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33538     },
33539     
33540     /**
33541      * Returns true if this node is currently loading
33542      * @return {Boolean}
33543      */
33544     isLoading : function(){
33545         return this.loading;  
33546     },
33547     
33548     loadComplete : function(deep, anim, callback){
33549         this.loading = false;
33550         this.loaded = true;
33551         this.ui.afterLoad(this);
33552         this.fireEvent("load", this);
33553         this.expand(deep, anim, callback);
33554     },
33555     
33556     /**
33557      * Returns true if this node has been loaded
33558      * @return {Boolean}
33559      */
33560     isLoaded : function(){
33561         return this.loaded;
33562     },
33563     
33564     hasChildNodes : function(){
33565         if(!this.isLeaf() && !this.loaded){
33566             return true;
33567         }else{
33568             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33569         }
33570     },
33571
33572     /**
33573      * Trigger a reload for this node
33574      * @param {Function} callback
33575      */
33576     reload : function(callback){
33577         this.collapse(false, false);
33578         while(this.firstChild){
33579             this.removeChild(this.firstChild);
33580         }
33581         this.childrenRendered = false;
33582         this.loaded = false;
33583         if(this.isHiddenRoot()){
33584             this.expanded = false;
33585         }
33586         this.expand(false, false, callback);
33587     }
33588 });/*
33589  * Based on:
33590  * Ext JS Library 1.1.1
33591  * Copyright(c) 2006-2007, Ext JS, LLC.
33592  *
33593  * Originally Released Under LGPL - original licence link has changed is not relivant.
33594  *
33595  * Fork - LGPL
33596  * <script type="text/javascript">
33597  */
33598  
33599 /**
33600  * @class Roo.tree.TreeNodeUI
33601  * @constructor
33602  * @param {Object} node The node to render
33603  * The TreeNode UI implementation is separate from the
33604  * tree implementation. Unless you are customizing the tree UI,
33605  * you should never have to use this directly.
33606  */
33607 Roo.tree.TreeNodeUI = function(node){
33608     this.node = node;
33609     this.rendered = false;
33610     this.animating = false;
33611     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33612 };
33613
33614 Roo.tree.TreeNodeUI.prototype = {
33615     removeChild : function(node){
33616         if(this.rendered){
33617             this.ctNode.removeChild(node.ui.getEl());
33618         }
33619     },
33620
33621     beforeLoad : function(){
33622          this.addClass("x-tree-node-loading");
33623     },
33624
33625     afterLoad : function(){
33626          this.removeClass("x-tree-node-loading");
33627     },
33628
33629     onTextChange : function(node, text, oldText){
33630         if(this.rendered){
33631             this.textNode.innerHTML = text;
33632         }
33633     },
33634
33635     onDisableChange : function(node, state){
33636         this.disabled = state;
33637         if(state){
33638             this.addClass("x-tree-node-disabled");
33639         }else{
33640             this.removeClass("x-tree-node-disabled");
33641         }
33642     },
33643
33644     onSelectedChange : function(state){
33645         if(state){
33646             this.focus();
33647             this.addClass("x-tree-selected");
33648         }else{
33649             //this.blur();
33650             this.removeClass("x-tree-selected");
33651         }
33652     },
33653
33654     onMove : function(tree, node, oldParent, newParent, index, refNode){
33655         this.childIndent = null;
33656         if(this.rendered){
33657             var targetNode = newParent.ui.getContainer();
33658             if(!targetNode){//target not rendered
33659                 this.holder = document.createElement("div");
33660                 this.holder.appendChild(this.wrap);
33661                 return;
33662             }
33663             var insertBefore = refNode ? refNode.ui.getEl() : null;
33664             if(insertBefore){
33665                 targetNode.insertBefore(this.wrap, insertBefore);
33666             }else{
33667                 targetNode.appendChild(this.wrap);
33668             }
33669             this.node.renderIndent(true);
33670         }
33671     },
33672
33673     addClass : function(cls){
33674         if(this.elNode){
33675             Roo.fly(this.elNode).addClass(cls);
33676         }
33677     },
33678
33679     removeClass : function(cls){
33680         if(this.elNode){
33681             Roo.fly(this.elNode).removeClass(cls);
33682         }
33683     },
33684
33685     remove : function(){
33686         if(this.rendered){
33687             this.holder = document.createElement("div");
33688             this.holder.appendChild(this.wrap);
33689         }
33690     },
33691
33692     fireEvent : function(){
33693         return this.node.fireEvent.apply(this.node, arguments);
33694     },
33695
33696     initEvents : function(){
33697         this.node.on("move", this.onMove, this);
33698         var E = Roo.EventManager;
33699         var a = this.anchor;
33700
33701         var el = Roo.fly(a, '_treeui');
33702
33703         if(Roo.isOpera){ // opera render bug ignores the CSS
33704             el.setStyle("text-decoration", "none");
33705         }
33706
33707         el.on("click", this.onClick, this);
33708         el.on("dblclick", this.onDblClick, this);
33709
33710         if(this.checkbox){
33711             Roo.EventManager.on(this.checkbox,
33712                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33713         }
33714
33715         el.on("contextmenu", this.onContextMenu, this);
33716
33717         var icon = Roo.fly(this.iconNode);
33718         icon.on("click", this.onClick, this);
33719         icon.on("dblclick", this.onDblClick, this);
33720         icon.on("contextmenu", this.onContextMenu, this);
33721         E.on(this.ecNode, "click", this.ecClick, this, true);
33722
33723         if(this.node.disabled){
33724             this.addClass("x-tree-node-disabled");
33725         }
33726         if(this.node.hidden){
33727             this.addClass("x-tree-node-disabled");
33728         }
33729         var ot = this.node.getOwnerTree();
33730         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33731         if(dd && (!this.node.isRoot || ot.rootVisible)){
33732             Roo.dd.Registry.register(this.elNode, {
33733                 node: this.node,
33734                 handles: this.getDDHandles(),
33735                 isHandle: false
33736             });
33737         }
33738     },
33739
33740     getDDHandles : function(){
33741         return [this.iconNode, this.textNode];
33742     },
33743
33744     hide : function(){
33745         if(this.rendered){
33746             this.wrap.style.display = "none";
33747         }
33748     },
33749
33750     show : function(){
33751         if(this.rendered){
33752             this.wrap.style.display = "";
33753         }
33754     },
33755
33756     onContextMenu : function(e){
33757         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33758             e.preventDefault();
33759             this.focus();
33760             this.fireEvent("contextmenu", this.node, e);
33761         }
33762     },
33763
33764     onClick : function(e){
33765         if(this.dropping){
33766             e.stopEvent();
33767             return;
33768         }
33769         if(this.fireEvent("beforeclick", this.node, e) !== false){
33770             if(!this.disabled && this.node.attributes.href){
33771                 this.fireEvent("click", this.node, e);
33772                 return;
33773             }
33774             e.preventDefault();
33775             if(this.disabled){
33776                 return;
33777             }
33778
33779             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33780                 this.node.toggle();
33781             }
33782
33783             this.fireEvent("click", this.node, e);
33784         }else{
33785             e.stopEvent();
33786         }
33787     },
33788
33789     onDblClick : function(e){
33790         e.preventDefault();
33791         if(this.disabled){
33792             return;
33793         }
33794         if(this.checkbox){
33795             this.toggleCheck();
33796         }
33797         if(!this.animating && this.node.hasChildNodes()){
33798             this.node.toggle();
33799         }
33800         this.fireEvent("dblclick", this.node, e);
33801     },
33802
33803     onCheckChange : function(){
33804         var checked = this.checkbox.checked;
33805         this.node.attributes.checked = checked;
33806         this.fireEvent('checkchange', this.node, checked);
33807     },
33808
33809     ecClick : function(e){
33810         if(!this.animating && this.node.hasChildNodes()){
33811             this.node.toggle();
33812         }
33813     },
33814
33815     startDrop : function(){
33816         this.dropping = true;
33817     },
33818
33819     // delayed drop so the click event doesn't get fired on a drop
33820     endDrop : function(){
33821        setTimeout(function(){
33822            this.dropping = false;
33823        }.createDelegate(this), 50);
33824     },
33825
33826     expand : function(){
33827         this.updateExpandIcon();
33828         this.ctNode.style.display = "";
33829     },
33830
33831     focus : function(){
33832         if(!this.node.preventHScroll){
33833             try{this.anchor.focus();
33834             }catch(e){}
33835         }else if(!Roo.isIE){
33836             try{
33837                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33838                 var l = noscroll.scrollLeft;
33839                 this.anchor.focus();
33840                 noscroll.scrollLeft = l;
33841             }catch(e){}
33842         }
33843     },
33844
33845     toggleCheck : function(value){
33846         var cb = this.checkbox;
33847         if(cb){
33848             cb.checked = (value === undefined ? !cb.checked : value);
33849         }
33850     },
33851
33852     blur : function(){
33853         try{
33854             this.anchor.blur();
33855         }catch(e){}
33856     },
33857
33858     animExpand : function(callback){
33859         var ct = Roo.get(this.ctNode);
33860         ct.stopFx();
33861         if(!this.node.hasChildNodes()){
33862             this.updateExpandIcon();
33863             this.ctNode.style.display = "";
33864             Roo.callback(callback);
33865             return;
33866         }
33867         this.animating = true;
33868         this.updateExpandIcon();
33869
33870         ct.slideIn('t', {
33871            callback : function(){
33872                this.animating = false;
33873                Roo.callback(callback);
33874             },
33875             scope: this,
33876             duration: this.node.ownerTree.duration || .25
33877         });
33878     },
33879
33880     highlight : function(){
33881         var tree = this.node.getOwnerTree();
33882         Roo.fly(this.wrap).highlight(
33883             tree.hlColor || "C3DAF9",
33884             {endColor: tree.hlBaseColor}
33885         );
33886     },
33887
33888     collapse : function(){
33889         this.updateExpandIcon();
33890         this.ctNode.style.display = "none";
33891     },
33892
33893     animCollapse : function(callback){
33894         var ct = Roo.get(this.ctNode);
33895         ct.enableDisplayMode('block');
33896         ct.stopFx();
33897
33898         this.animating = true;
33899         this.updateExpandIcon();
33900
33901         ct.slideOut('t', {
33902             callback : function(){
33903                this.animating = false;
33904                Roo.callback(callback);
33905             },
33906             scope: this,
33907             duration: this.node.ownerTree.duration || .25
33908         });
33909     },
33910
33911     getContainer : function(){
33912         return this.ctNode;
33913     },
33914
33915     getEl : function(){
33916         return this.wrap;
33917     },
33918
33919     appendDDGhost : function(ghostNode){
33920         ghostNode.appendChild(this.elNode.cloneNode(true));
33921     },
33922
33923     getDDRepairXY : function(){
33924         return Roo.lib.Dom.getXY(this.iconNode);
33925     },
33926
33927     onRender : function(){
33928         this.render();
33929     },
33930
33931     render : function(bulkRender){
33932         var n = this.node, a = n.attributes;
33933         var targetNode = n.parentNode ?
33934               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33935
33936         if(!this.rendered){
33937             this.rendered = true;
33938
33939             this.renderElements(n, a, targetNode, bulkRender);
33940
33941             if(a.qtip){
33942                if(this.textNode.setAttributeNS){
33943                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33944                    if(a.qtipTitle){
33945                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33946                    }
33947                }else{
33948                    this.textNode.setAttribute("ext:qtip", a.qtip);
33949                    if(a.qtipTitle){
33950                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33951                    }
33952                }
33953             }else if(a.qtipCfg){
33954                 a.qtipCfg.target = Roo.id(this.textNode);
33955                 Roo.QuickTips.register(a.qtipCfg);
33956             }
33957             this.initEvents();
33958             if(!this.node.expanded){
33959                 this.updateExpandIcon();
33960             }
33961         }else{
33962             if(bulkRender === true) {
33963                 targetNode.appendChild(this.wrap);
33964             }
33965         }
33966     },
33967
33968     renderElements : function(n, a, targetNode, bulkRender)
33969     {
33970         // add some indent caching, this helps performance when rendering a large tree
33971         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33972         var t = n.getOwnerTree();
33973         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33974         if (typeof(n.attributes.html) != 'undefined') {
33975             txt = n.attributes.html;
33976         }
33977         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33978         var cb = typeof a.checked == 'boolean';
33979         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33980         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33981             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33982             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33983             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33984             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33985             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33986              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33987                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33988             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33989             "</li>"];
33990
33991         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33992             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33993                                 n.nextSibling.ui.getEl(), buf.join(""));
33994         }else{
33995             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33996         }
33997
33998         this.elNode = this.wrap.childNodes[0];
33999         this.ctNode = this.wrap.childNodes[1];
34000         var cs = this.elNode.childNodes;
34001         this.indentNode = cs[0];
34002         this.ecNode = cs[1];
34003         this.iconNode = cs[2];
34004         var index = 3;
34005         if(cb){
34006             this.checkbox = cs[3];
34007             index++;
34008         }
34009         this.anchor = cs[index];
34010         this.textNode = cs[index].firstChild;
34011     },
34012
34013     getAnchor : function(){
34014         return this.anchor;
34015     },
34016
34017     getTextEl : function(){
34018         return this.textNode;
34019     },
34020
34021     getIconEl : function(){
34022         return this.iconNode;
34023     },
34024
34025     isChecked : function(){
34026         return this.checkbox ? this.checkbox.checked : false;
34027     },
34028
34029     updateExpandIcon : function(){
34030         if(this.rendered){
34031             var n = this.node, c1, c2;
34032             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34033             var hasChild = n.hasChildNodes();
34034             if(hasChild){
34035                 if(n.expanded){
34036                     cls += "-minus";
34037                     c1 = "x-tree-node-collapsed";
34038                     c2 = "x-tree-node-expanded";
34039                 }else{
34040                     cls += "-plus";
34041                     c1 = "x-tree-node-expanded";
34042                     c2 = "x-tree-node-collapsed";
34043                 }
34044                 if(this.wasLeaf){
34045                     this.removeClass("x-tree-node-leaf");
34046                     this.wasLeaf = false;
34047                 }
34048                 if(this.c1 != c1 || this.c2 != c2){
34049                     Roo.fly(this.elNode).replaceClass(c1, c2);
34050                     this.c1 = c1; this.c2 = c2;
34051                 }
34052             }else{
34053                 // this changes non-leafs into leafs if they have no children.
34054                 // it's not very rational behaviour..
34055                 
34056                 if(!this.wasLeaf && this.node.leaf){
34057                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34058                     delete this.c1;
34059                     delete this.c2;
34060                     this.wasLeaf = true;
34061                 }
34062             }
34063             var ecc = "x-tree-ec-icon "+cls;
34064             if(this.ecc != ecc){
34065                 this.ecNode.className = ecc;
34066                 this.ecc = ecc;
34067             }
34068         }
34069     },
34070
34071     getChildIndent : function(){
34072         if(!this.childIndent){
34073             var buf = [];
34074             var p = this.node;
34075             while(p){
34076                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34077                     if(!p.isLast()) {
34078                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34079                     } else {
34080                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34081                     }
34082                 }
34083                 p = p.parentNode;
34084             }
34085             this.childIndent = buf.join("");
34086         }
34087         return this.childIndent;
34088     },
34089
34090     renderIndent : function(){
34091         if(this.rendered){
34092             var indent = "";
34093             var p = this.node.parentNode;
34094             if(p){
34095                 indent = p.ui.getChildIndent();
34096             }
34097             if(this.indentMarkup != indent){ // don't rerender if not required
34098                 this.indentNode.innerHTML = indent;
34099                 this.indentMarkup = indent;
34100             }
34101             this.updateExpandIcon();
34102         }
34103     }
34104 };
34105
34106 Roo.tree.RootTreeNodeUI = function(){
34107     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34108 };
34109 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34110     render : function(){
34111         if(!this.rendered){
34112             var targetNode = this.node.ownerTree.innerCt.dom;
34113             this.node.expanded = true;
34114             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34115             this.wrap = this.ctNode = targetNode.firstChild;
34116         }
34117     },
34118     collapse : function(){
34119     },
34120     expand : function(){
34121     }
34122 });/*
34123  * Based on:
34124  * Ext JS Library 1.1.1
34125  * Copyright(c) 2006-2007, Ext JS, LLC.
34126  *
34127  * Originally Released Under LGPL - original licence link has changed is not relivant.
34128  *
34129  * Fork - LGPL
34130  * <script type="text/javascript">
34131  */
34132 /**
34133  * @class Roo.tree.TreeLoader
34134  * @extends Roo.util.Observable
34135  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34136  * nodes from a specified URL. The response must be a javascript Array definition
34137  * who's elements are node definition objects. eg:
34138  * <pre><code>
34139 {  success : true,
34140    data :      [
34141    
34142     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34143     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34144     ]
34145 }
34146
34147
34148 </code></pre>
34149  * <br><br>
34150  * The old style respose with just an array is still supported, but not recommended.
34151  * <br><br>
34152  *
34153  * A server request is sent, and child nodes are loaded only when a node is expanded.
34154  * The loading node's id is passed to the server under the parameter name "node" to
34155  * enable the server to produce the correct child nodes.
34156  * <br><br>
34157  * To pass extra parameters, an event handler may be attached to the "beforeload"
34158  * event, and the parameters specified in the TreeLoader's baseParams property:
34159  * <pre><code>
34160     myTreeLoader.on("beforeload", function(treeLoader, node) {
34161         this.baseParams.category = node.attributes.category;
34162     }, this);
34163 </code></pre><
34164  * This would pass an HTTP parameter called "category" to the server containing
34165  * the value of the Node's "category" attribute.
34166  * @constructor
34167  * Creates a new Treeloader.
34168  * @param {Object} config A config object containing config properties.
34169  */
34170 Roo.tree.TreeLoader = function(config){
34171     this.baseParams = {};
34172     this.requestMethod = "POST";
34173     Roo.apply(this, config);
34174
34175     this.addEvents({
34176     
34177         /**
34178          * @event beforeload
34179          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34180          * @param {Object} This TreeLoader object.
34181          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34182          * @param {Object} callback The callback function specified in the {@link #load} call.
34183          */
34184         beforeload : true,
34185         /**
34186          * @event load
34187          * Fires when the node has been successfuly loaded.
34188          * @param {Object} This TreeLoader object.
34189          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34190          * @param {Object} response The response object containing the data from the server.
34191          */
34192         load : true,
34193         /**
34194          * @event loadexception
34195          * Fires if the network request failed.
34196          * @param {Object} This TreeLoader object.
34197          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34198          * @param {Object} response The response object containing the data from the server.
34199          */
34200         loadexception : true,
34201         /**
34202          * @event create
34203          * Fires before a node is created, enabling you to return custom Node types 
34204          * @param {Object} This TreeLoader object.
34205          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34206          */
34207         create : true
34208     });
34209
34210     Roo.tree.TreeLoader.superclass.constructor.call(this);
34211 };
34212
34213 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34214     /**
34215     * @cfg {String} dataUrl The URL from which to request a Json string which
34216     * specifies an array of node definition object representing the child nodes
34217     * to be loaded.
34218     */
34219     /**
34220     * @cfg {String} requestMethod either GET or POST
34221     * defaults to POST (due to BC)
34222     * to be loaded.
34223     */
34224     /**
34225     * @cfg {Object} baseParams (optional) An object containing properties which
34226     * specify HTTP parameters to be passed to each request for child nodes.
34227     */
34228     /**
34229     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34230     * created by this loader. If the attributes sent by the server have an attribute in this object,
34231     * they take priority.
34232     */
34233     /**
34234     * @cfg {Object} uiProviders (optional) An object containing properties which
34235     * 
34236     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34237     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34238     * <i>uiProvider</i> attribute of a returned child node is a string rather
34239     * than a reference to a TreeNodeUI implementation, this that string value
34240     * is used as a property name in the uiProviders object. You can define the provider named
34241     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34242     */
34243     uiProviders : {},
34244
34245     /**
34246     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34247     * child nodes before loading.
34248     */
34249     clearOnLoad : true,
34250
34251     /**
34252     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34253     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34254     * Grid query { data : [ .....] }
34255     */
34256     
34257     root : false,
34258      /**
34259     * @cfg {String} queryParam (optional) 
34260     * Name of the query as it will be passed on the querystring (defaults to 'node')
34261     * eg. the request will be ?node=[id]
34262     */
34263     
34264     
34265     queryParam: false,
34266     
34267     /**
34268      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34269      * This is called automatically when a node is expanded, but may be used to reload
34270      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34271      * @param {Roo.tree.TreeNode} node
34272      * @param {Function} callback
34273      */
34274     load : function(node, callback){
34275         if(this.clearOnLoad){
34276             while(node.firstChild){
34277                 node.removeChild(node.firstChild);
34278             }
34279         }
34280         if(node.attributes.children){ // preloaded json children
34281             var cs = node.attributes.children;
34282             for(var i = 0, len = cs.length; i < len; i++){
34283                 node.appendChild(this.createNode(cs[i]));
34284             }
34285             if(typeof callback == "function"){
34286                 callback();
34287             }
34288         }else if(this.dataUrl){
34289             this.requestData(node, callback);
34290         }
34291     },
34292
34293     getParams: function(node){
34294         var buf = [], bp = this.baseParams;
34295         for(var key in bp){
34296             if(typeof bp[key] != "function"){
34297                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34298             }
34299         }
34300         var n = this.queryParam === false ? 'node' : this.queryParam;
34301         buf.push(n + "=", encodeURIComponent(node.id));
34302         return buf.join("");
34303     },
34304
34305     requestData : function(node, callback){
34306         if(this.fireEvent("beforeload", this, node, callback) !== false){
34307             this.transId = Roo.Ajax.request({
34308                 method:this.requestMethod,
34309                 url: this.dataUrl||this.url,
34310                 success: this.handleResponse,
34311                 failure: this.handleFailure,
34312                 scope: this,
34313                 argument: {callback: callback, node: node},
34314                 params: this.getParams(node)
34315             });
34316         }else{
34317             // if the load is cancelled, make sure we notify
34318             // the node that we are done
34319             if(typeof callback == "function"){
34320                 callback();
34321             }
34322         }
34323     },
34324
34325     isLoading : function(){
34326         return this.transId ? true : false;
34327     },
34328
34329     abort : function(){
34330         if(this.isLoading()){
34331             Roo.Ajax.abort(this.transId);
34332         }
34333     },
34334
34335     // private
34336     createNode : function(attr)
34337     {
34338         // apply baseAttrs, nice idea Corey!
34339         if(this.baseAttrs){
34340             Roo.applyIf(attr, this.baseAttrs);
34341         }
34342         if(this.applyLoader !== false){
34343             attr.loader = this;
34344         }
34345         // uiProvider = depreciated..
34346         
34347         if(typeof(attr.uiProvider) == 'string'){
34348            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34349                 /**  eval:var:attr */ eval(attr.uiProvider);
34350         }
34351         if(typeof(this.uiProviders['default']) != 'undefined') {
34352             attr.uiProvider = this.uiProviders['default'];
34353         }
34354         
34355         this.fireEvent('create', this, attr);
34356         
34357         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34358         return(attr.leaf ?
34359                         new Roo.tree.TreeNode(attr) :
34360                         new Roo.tree.AsyncTreeNode(attr));
34361     },
34362
34363     processResponse : function(response, node, callback)
34364     {
34365         var json = response.responseText;
34366         try {
34367             
34368             var o = Roo.decode(json);
34369             
34370             if (this.root === false && typeof(o.success) != undefined) {
34371                 this.root = 'data'; // the default behaviour for list like data..
34372                 }
34373                 
34374             if (this.root !== false &&  !o.success) {
34375                 // it's a failure condition.
34376                 var a = response.argument;
34377                 this.fireEvent("loadexception", this, a.node, response);
34378                 Roo.log("Load failed - should have a handler really");
34379                 return;
34380             }
34381             
34382             
34383             
34384             if (this.root !== false) {
34385                  o = o[this.root];
34386             }
34387             
34388             for(var i = 0, len = o.length; i < len; i++){
34389                 var n = this.createNode(o[i]);
34390                 if(n){
34391                     node.appendChild(n);
34392                 }
34393             }
34394             if(typeof callback == "function"){
34395                 callback(this, node);
34396             }
34397         }catch(e){
34398             this.handleFailure(response);
34399         }
34400     },
34401
34402     handleResponse : function(response){
34403         this.transId = false;
34404         var a = response.argument;
34405         this.processResponse(response, a.node, a.callback);
34406         this.fireEvent("load", this, a.node, response);
34407     },
34408
34409     handleFailure : function(response)
34410     {
34411         // should handle failure better..
34412         this.transId = false;
34413         var a = response.argument;
34414         this.fireEvent("loadexception", this, a.node, response);
34415         if(typeof a.callback == "function"){
34416             a.callback(this, a.node);
34417         }
34418     }
34419 });/*
34420  * Based on:
34421  * Ext JS Library 1.1.1
34422  * Copyright(c) 2006-2007, Ext JS, LLC.
34423  *
34424  * Originally Released Under LGPL - original licence link has changed is not relivant.
34425  *
34426  * Fork - LGPL
34427  * <script type="text/javascript">
34428  */
34429
34430 /**
34431 * @class Roo.tree.TreeFilter
34432 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34433 * @param {TreePanel} tree
34434 * @param {Object} config (optional)
34435  */
34436 Roo.tree.TreeFilter = function(tree, config){
34437     this.tree = tree;
34438     this.filtered = {};
34439     Roo.apply(this, config);
34440 };
34441
34442 Roo.tree.TreeFilter.prototype = {
34443     clearBlank:false,
34444     reverse:false,
34445     autoClear:false,
34446     remove:false,
34447
34448      /**
34449      * Filter the data by a specific attribute.
34450      * @param {String/RegExp} value Either string that the attribute value
34451      * should start with or a RegExp to test against the attribute
34452      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34453      * @param {TreeNode} startNode (optional) The node to start the filter at.
34454      */
34455     filter : function(value, attr, startNode){
34456         attr = attr || "text";
34457         var f;
34458         if(typeof value == "string"){
34459             var vlen = value.length;
34460             // auto clear empty filter
34461             if(vlen == 0 && this.clearBlank){
34462                 this.clear();
34463                 return;
34464             }
34465             value = value.toLowerCase();
34466             f = function(n){
34467                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34468             };
34469         }else if(value.exec){ // regex?
34470             f = function(n){
34471                 return value.test(n.attributes[attr]);
34472             };
34473         }else{
34474             throw 'Illegal filter type, must be string or regex';
34475         }
34476         this.filterBy(f, null, startNode);
34477         },
34478
34479     /**
34480      * Filter by a function. The passed function will be called with each
34481      * node in the tree (or from the startNode). If the function returns true, the node is kept
34482      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34483      * @param {Function} fn The filter function
34484      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34485      */
34486     filterBy : function(fn, scope, startNode){
34487         startNode = startNode || this.tree.root;
34488         if(this.autoClear){
34489             this.clear();
34490         }
34491         var af = this.filtered, rv = this.reverse;
34492         var f = function(n){
34493             if(n == startNode){
34494                 return true;
34495             }
34496             if(af[n.id]){
34497                 return false;
34498             }
34499             var m = fn.call(scope || n, n);
34500             if(!m || rv){
34501                 af[n.id] = n;
34502                 n.ui.hide();
34503                 return false;
34504             }
34505             return true;
34506         };
34507         startNode.cascade(f);
34508         if(this.remove){
34509            for(var id in af){
34510                if(typeof id != "function"){
34511                    var n = af[id];
34512                    if(n && n.parentNode){
34513                        n.parentNode.removeChild(n);
34514                    }
34515                }
34516            }
34517         }
34518     },
34519
34520     /**
34521      * Clears the current filter. Note: with the "remove" option
34522      * set a filter cannot be cleared.
34523      */
34524     clear : function(){
34525         var t = this.tree;
34526         var af = this.filtered;
34527         for(var id in af){
34528             if(typeof id != "function"){
34529                 var n = af[id];
34530                 if(n){
34531                     n.ui.show();
34532                 }
34533             }
34534         }
34535         this.filtered = {};
34536     }
34537 };
34538 /*
34539  * Based on:
34540  * Ext JS Library 1.1.1
34541  * Copyright(c) 2006-2007, Ext JS, LLC.
34542  *
34543  * Originally Released Under LGPL - original licence link has changed is not relivant.
34544  *
34545  * Fork - LGPL
34546  * <script type="text/javascript">
34547  */
34548  
34549
34550 /**
34551  * @class Roo.tree.TreeSorter
34552  * Provides sorting of nodes in a TreePanel
34553  * 
34554  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34555  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34556  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34557  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34558  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34559  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34560  * @constructor
34561  * @param {TreePanel} tree
34562  * @param {Object} config
34563  */
34564 Roo.tree.TreeSorter = function(tree, config){
34565     Roo.apply(this, config);
34566     tree.on("beforechildrenrendered", this.doSort, this);
34567     tree.on("append", this.updateSort, this);
34568     tree.on("insert", this.updateSort, this);
34569     
34570     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34571     var p = this.property || "text";
34572     var sortType = this.sortType;
34573     var fs = this.folderSort;
34574     var cs = this.caseSensitive === true;
34575     var leafAttr = this.leafAttr || 'leaf';
34576
34577     this.sortFn = function(n1, n2){
34578         if(fs){
34579             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34580                 return 1;
34581             }
34582             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34583                 return -1;
34584             }
34585         }
34586         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34587         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34588         if(v1 < v2){
34589                         return dsc ? +1 : -1;
34590                 }else if(v1 > v2){
34591                         return dsc ? -1 : +1;
34592         }else{
34593                 return 0;
34594         }
34595     };
34596 };
34597
34598 Roo.tree.TreeSorter.prototype = {
34599     doSort : function(node){
34600         node.sort(this.sortFn);
34601     },
34602     
34603     compareNodes : function(n1, n2){
34604         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34605     },
34606     
34607     updateSort : function(tree, node){
34608         if(node.childrenRendered){
34609             this.doSort.defer(1, this, [node]);
34610         }
34611     }
34612 };/*
34613  * Based on:
34614  * Ext JS Library 1.1.1
34615  * Copyright(c) 2006-2007, Ext JS, LLC.
34616  *
34617  * Originally Released Under LGPL - original licence link has changed is not relivant.
34618  *
34619  * Fork - LGPL
34620  * <script type="text/javascript">
34621  */
34622
34623 if(Roo.dd.DropZone){
34624     
34625 Roo.tree.TreeDropZone = function(tree, config){
34626     this.allowParentInsert = false;
34627     this.allowContainerDrop = false;
34628     this.appendOnly = false;
34629     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34630     this.tree = tree;
34631     this.lastInsertClass = "x-tree-no-status";
34632     this.dragOverData = {};
34633 };
34634
34635 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34636     ddGroup : "TreeDD",
34637     scroll:  true,
34638     
34639     expandDelay : 1000,
34640     
34641     expandNode : function(node){
34642         if(node.hasChildNodes() && !node.isExpanded()){
34643             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34644         }
34645     },
34646     
34647     queueExpand : function(node){
34648         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34649     },
34650     
34651     cancelExpand : function(){
34652         if(this.expandProcId){
34653             clearTimeout(this.expandProcId);
34654             this.expandProcId = false;
34655         }
34656     },
34657     
34658     isValidDropPoint : function(n, pt, dd, e, data){
34659         if(!n || !data){ return false; }
34660         var targetNode = n.node;
34661         var dropNode = data.node;
34662         // default drop rules
34663         if(!(targetNode && targetNode.isTarget && pt)){
34664             return false;
34665         }
34666         if(pt == "append" && targetNode.allowChildren === false){
34667             return false;
34668         }
34669         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34670             return false;
34671         }
34672         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34673             return false;
34674         }
34675         // reuse the object
34676         var overEvent = this.dragOverData;
34677         overEvent.tree = this.tree;
34678         overEvent.target = targetNode;
34679         overEvent.data = data;
34680         overEvent.point = pt;
34681         overEvent.source = dd;
34682         overEvent.rawEvent = e;
34683         overEvent.dropNode = dropNode;
34684         overEvent.cancel = false;  
34685         var result = this.tree.fireEvent("nodedragover", overEvent);
34686         return overEvent.cancel === false && result !== false;
34687     },
34688     
34689     getDropPoint : function(e, n, dd)
34690     {
34691         var tn = n.node;
34692         if(tn.isRoot){
34693             return tn.allowChildren !== false ? "append" : false; // always append for root
34694         }
34695         var dragEl = n.ddel;
34696         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34697         var y = Roo.lib.Event.getPageY(e);
34698         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34699         
34700         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34701         var noAppend = tn.allowChildren === false;
34702         if(this.appendOnly || tn.parentNode.allowChildren === false){
34703             return noAppend ? false : "append";
34704         }
34705         var noBelow = false;
34706         if(!this.allowParentInsert){
34707             noBelow = tn.hasChildNodes() && tn.isExpanded();
34708         }
34709         var q = (b - t) / (noAppend ? 2 : 3);
34710         if(y >= t && y < (t + q)){
34711             return "above";
34712         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34713             return "below";
34714         }else{
34715             return "append";
34716         }
34717     },
34718     
34719     onNodeEnter : function(n, dd, e, data)
34720     {
34721         this.cancelExpand();
34722     },
34723     
34724     onNodeOver : function(n, dd, e, data)
34725     {
34726        
34727         var pt = this.getDropPoint(e, n, dd);
34728         var node = n.node;
34729         
34730         // auto node expand check
34731         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34732             this.queueExpand(node);
34733         }else if(pt != "append"){
34734             this.cancelExpand();
34735         }
34736         
34737         // set the insert point style on the target node
34738         var returnCls = this.dropNotAllowed;
34739         if(this.isValidDropPoint(n, pt, dd, e, data)){
34740            if(pt){
34741                var el = n.ddel;
34742                var cls;
34743                if(pt == "above"){
34744                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34745                    cls = "x-tree-drag-insert-above";
34746                }else if(pt == "below"){
34747                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34748                    cls = "x-tree-drag-insert-below";
34749                }else{
34750                    returnCls = "x-tree-drop-ok-append";
34751                    cls = "x-tree-drag-append";
34752                }
34753                if(this.lastInsertClass != cls){
34754                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34755                    this.lastInsertClass = cls;
34756                }
34757            }
34758        }
34759        return returnCls;
34760     },
34761     
34762     onNodeOut : function(n, dd, e, data){
34763         
34764         this.cancelExpand();
34765         this.removeDropIndicators(n);
34766     },
34767     
34768     onNodeDrop : function(n, dd, e, data){
34769         var point = this.getDropPoint(e, n, dd);
34770         var targetNode = n.node;
34771         targetNode.ui.startDrop();
34772         if(!this.isValidDropPoint(n, point, dd, e, data)){
34773             targetNode.ui.endDrop();
34774             return false;
34775         }
34776         // first try to find the drop node
34777         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34778         var dropEvent = {
34779             tree : this.tree,
34780             target: targetNode,
34781             data: data,
34782             point: point,
34783             source: dd,
34784             rawEvent: e,
34785             dropNode: dropNode,
34786             cancel: !dropNode   
34787         };
34788         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34789         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34790             targetNode.ui.endDrop();
34791             return false;
34792         }
34793         // allow target changing
34794         targetNode = dropEvent.target;
34795         if(point == "append" && !targetNode.isExpanded()){
34796             targetNode.expand(false, null, function(){
34797                 this.completeDrop(dropEvent);
34798             }.createDelegate(this));
34799         }else{
34800             this.completeDrop(dropEvent);
34801         }
34802         return true;
34803     },
34804     
34805     completeDrop : function(de){
34806         var ns = de.dropNode, p = de.point, t = de.target;
34807         if(!(ns instanceof Array)){
34808             ns = [ns];
34809         }
34810         var n;
34811         for(var i = 0, len = ns.length; i < len; i++){
34812             n = ns[i];
34813             if(p == "above"){
34814                 t.parentNode.insertBefore(n, t);
34815             }else if(p == "below"){
34816                 t.parentNode.insertBefore(n, t.nextSibling);
34817             }else{
34818                 t.appendChild(n);
34819             }
34820         }
34821         n.ui.focus();
34822         if(this.tree.hlDrop){
34823             n.ui.highlight();
34824         }
34825         t.ui.endDrop();
34826         this.tree.fireEvent("nodedrop", de);
34827     },
34828     
34829     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34830         if(this.tree.hlDrop){
34831             dropNode.ui.focus();
34832             dropNode.ui.highlight();
34833         }
34834         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34835     },
34836     
34837     getTree : function(){
34838         return this.tree;
34839     },
34840     
34841     removeDropIndicators : function(n){
34842         if(n && n.ddel){
34843             var el = n.ddel;
34844             Roo.fly(el).removeClass([
34845                     "x-tree-drag-insert-above",
34846                     "x-tree-drag-insert-below",
34847                     "x-tree-drag-append"]);
34848             this.lastInsertClass = "_noclass";
34849         }
34850     },
34851     
34852     beforeDragDrop : function(target, e, id){
34853         this.cancelExpand();
34854         return true;
34855     },
34856     
34857     afterRepair : function(data){
34858         if(data && Roo.enableFx){
34859             data.node.ui.highlight();
34860         }
34861         this.hideProxy();
34862     } 
34863     
34864 });
34865
34866 }
34867 /*
34868  * Based on:
34869  * Ext JS Library 1.1.1
34870  * Copyright(c) 2006-2007, Ext JS, LLC.
34871  *
34872  * Originally Released Under LGPL - original licence link has changed is not relivant.
34873  *
34874  * Fork - LGPL
34875  * <script type="text/javascript">
34876  */
34877  
34878
34879 if(Roo.dd.DragZone){
34880 Roo.tree.TreeDragZone = function(tree, config){
34881     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34882     this.tree = tree;
34883 };
34884
34885 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34886     ddGroup : "TreeDD",
34887    
34888     onBeforeDrag : function(data, e){
34889         var n = data.node;
34890         return n && n.draggable && !n.disabled;
34891     },
34892      
34893     
34894     onInitDrag : function(e){
34895         var data = this.dragData;
34896         this.tree.getSelectionModel().select(data.node);
34897         this.proxy.update("");
34898         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34899         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34900     },
34901     
34902     getRepairXY : function(e, data){
34903         return data.node.ui.getDDRepairXY();
34904     },
34905     
34906     onEndDrag : function(data, e){
34907         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34908         
34909         
34910     },
34911     
34912     onValidDrop : function(dd, e, id){
34913         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34914         this.hideProxy();
34915     },
34916     
34917     beforeInvalidDrop : function(e, id){
34918         // this scrolls the original position back into view
34919         var sm = this.tree.getSelectionModel();
34920         sm.clearSelections();
34921         sm.select(this.dragData.node);
34922     }
34923 });
34924 }/*
34925  * Based on:
34926  * Ext JS Library 1.1.1
34927  * Copyright(c) 2006-2007, Ext JS, LLC.
34928  *
34929  * Originally Released Under LGPL - original licence link has changed is not relivant.
34930  *
34931  * Fork - LGPL
34932  * <script type="text/javascript">
34933  */
34934 /**
34935  * @class Roo.tree.TreeEditor
34936  * @extends Roo.Editor
34937  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34938  * as the editor field.
34939  * @constructor
34940  * @param {Object} config (used to be the tree panel.)
34941  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34942  * 
34943  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34944  * @cfg {Roo.form.TextField|Object} field The field configuration
34945  *
34946  * 
34947  */
34948 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34949     var tree = config;
34950     var field;
34951     if (oldconfig) { // old style..
34952         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34953     } else {
34954         // new style..
34955         tree = config.tree;
34956         config.field = config.field  || {};
34957         config.field.xtype = 'TextField';
34958         field = Roo.factory(config.field, Roo.form);
34959     }
34960     config = config || {};
34961     
34962     
34963     this.addEvents({
34964         /**
34965          * @event beforenodeedit
34966          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34967          * false from the handler of this event.
34968          * @param {Editor} this
34969          * @param {Roo.tree.Node} node 
34970          */
34971         "beforenodeedit" : true
34972     });
34973     
34974     //Roo.log(config);
34975     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34976
34977     this.tree = tree;
34978
34979     tree.on('beforeclick', this.beforeNodeClick, this);
34980     tree.getTreeEl().on('mousedown', this.hide, this);
34981     this.on('complete', this.updateNode, this);
34982     this.on('beforestartedit', this.fitToTree, this);
34983     this.on('startedit', this.bindScroll, this, {delay:10});
34984     this.on('specialkey', this.onSpecialKey, this);
34985 };
34986
34987 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34988     /**
34989      * @cfg {String} alignment
34990      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34991      */
34992     alignment: "l-l",
34993     // inherit
34994     autoSize: false,
34995     /**
34996      * @cfg {Boolean} hideEl
34997      * True to hide the bound element while the editor is displayed (defaults to false)
34998      */
34999     hideEl : false,
35000     /**
35001      * @cfg {String} cls
35002      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35003      */
35004     cls: "x-small-editor x-tree-editor",
35005     /**
35006      * @cfg {Boolean} shim
35007      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35008      */
35009     shim:false,
35010     // inherit
35011     shadow:"frame",
35012     /**
35013      * @cfg {Number} maxWidth
35014      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35015      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35016      * scroll and client offsets into account prior to each edit.
35017      */
35018     maxWidth: 250,
35019
35020     editDelay : 350,
35021
35022     // private
35023     fitToTree : function(ed, el){
35024         var td = this.tree.getTreeEl().dom, nd = el.dom;
35025         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35026             td.scrollLeft = nd.offsetLeft;
35027         }
35028         var w = Math.min(
35029                 this.maxWidth,
35030                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35031         this.setSize(w, '');
35032         
35033         return this.fireEvent('beforenodeedit', this, this.editNode);
35034         
35035     },
35036
35037     // private
35038     triggerEdit : function(node){
35039         this.completeEdit();
35040         this.editNode = node;
35041         this.startEdit(node.ui.textNode, node.text);
35042     },
35043
35044     // private
35045     bindScroll : function(){
35046         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35047     },
35048
35049     // private
35050     beforeNodeClick : function(node, e){
35051         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35052         this.lastClick = new Date();
35053         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35054             e.stopEvent();
35055             this.triggerEdit(node);
35056             return false;
35057         }
35058         return true;
35059     },
35060
35061     // private
35062     updateNode : function(ed, value){
35063         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35064         this.editNode.setText(value);
35065     },
35066
35067     // private
35068     onHide : function(){
35069         Roo.tree.TreeEditor.superclass.onHide.call(this);
35070         if(this.editNode){
35071             this.editNode.ui.focus();
35072         }
35073     },
35074
35075     // private
35076     onSpecialKey : function(field, e){
35077         var k = e.getKey();
35078         if(k == e.ESC){
35079             e.stopEvent();
35080             this.cancelEdit();
35081         }else if(k == e.ENTER && !e.hasModifier()){
35082             e.stopEvent();
35083             this.completeEdit();
35084         }
35085     }
35086 });//<Script type="text/javascript">
35087 /*
35088  * Based on:
35089  * Ext JS Library 1.1.1
35090  * Copyright(c) 2006-2007, Ext JS, LLC.
35091  *
35092  * Originally Released Under LGPL - original licence link has changed is not relivant.
35093  *
35094  * Fork - LGPL
35095  * <script type="text/javascript">
35096  */
35097  
35098 /**
35099  * Not documented??? - probably should be...
35100  */
35101
35102 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35103     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35104     
35105     renderElements : function(n, a, targetNode, bulkRender){
35106         //consel.log("renderElements?");
35107         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35108
35109         var t = n.getOwnerTree();
35110         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35111         
35112         var cols = t.columns;
35113         var bw = t.borderWidth;
35114         var c = cols[0];
35115         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35116          var cb = typeof a.checked == "boolean";
35117         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35118         var colcls = 'x-t-' + tid + '-c0';
35119         var buf = [
35120             '<li class="x-tree-node">',
35121             
35122                 
35123                 '<div class="x-tree-node-el ', a.cls,'">',
35124                     // extran...
35125                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35126                 
35127                 
35128                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35129                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35130                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35131                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35132                            (a.iconCls ? ' '+a.iconCls : ''),
35133                            '" unselectable="on" />',
35134                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35135                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35136                              
35137                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35138                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35139                             '<span unselectable="on" qtip="' + tx + '">',
35140                              tx,
35141                              '</span></a>' ,
35142                     '</div>',
35143                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35144                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35145                  ];
35146         for(var i = 1, len = cols.length; i < len; i++){
35147             c = cols[i];
35148             colcls = 'x-t-' + tid + '-c' +i;
35149             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35150             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35151                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35152                       "</div>");
35153          }
35154          
35155          buf.push(
35156             '</a>',
35157             '<div class="x-clear"></div></div>',
35158             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35159             "</li>");
35160         
35161         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35162             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35163                                 n.nextSibling.ui.getEl(), buf.join(""));
35164         }else{
35165             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35166         }
35167         var el = this.wrap.firstChild;
35168         this.elRow = el;
35169         this.elNode = el.firstChild;
35170         this.ranchor = el.childNodes[1];
35171         this.ctNode = this.wrap.childNodes[1];
35172         var cs = el.firstChild.childNodes;
35173         this.indentNode = cs[0];
35174         this.ecNode = cs[1];
35175         this.iconNode = cs[2];
35176         var index = 3;
35177         if(cb){
35178             this.checkbox = cs[3];
35179             index++;
35180         }
35181         this.anchor = cs[index];
35182         
35183         this.textNode = cs[index].firstChild;
35184         
35185         //el.on("click", this.onClick, this);
35186         //el.on("dblclick", this.onDblClick, this);
35187         
35188         
35189        // console.log(this);
35190     },
35191     initEvents : function(){
35192         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35193         
35194             
35195         var a = this.ranchor;
35196
35197         var el = Roo.get(a);
35198
35199         if(Roo.isOpera){ // opera render bug ignores the CSS
35200             el.setStyle("text-decoration", "none");
35201         }
35202
35203         el.on("click", this.onClick, this);
35204         el.on("dblclick", this.onDblClick, this);
35205         el.on("contextmenu", this.onContextMenu, this);
35206         
35207     },
35208     
35209     /*onSelectedChange : function(state){
35210         if(state){
35211             this.focus();
35212             this.addClass("x-tree-selected");
35213         }else{
35214             //this.blur();
35215             this.removeClass("x-tree-selected");
35216         }
35217     },*/
35218     addClass : function(cls){
35219         if(this.elRow){
35220             Roo.fly(this.elRow).addClass(cls);
35221         }
35222         
35223     },
35224     
35225     
35226     removeClass : function(cls){
35227         if(this.elRow){
35228             Roo.fly(this.elRow).removeClass(cls);
35229         }
35230     }
35231
35232     
35233     
35234 });//<Script type="text/javascript">
35235
35236 /*
35237  * Based on:
35238  * Ext JS Library 1.1.1
35239  * Copyright(c) 2006-2007, Ext JS, LLC.
35240  *
35241  * Originally Released Under LGPL - original licence link has changed is not relivant.
35242  *
35243  * Fork - LGPL
35244  * <script type="text/javascript">
35245  */
35246  
35247
35248 /**
35249  * @class Roo.tree.ColumnTree
35250  * @extends Roo.data.TreePanel
35251  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35252  * @cfg {int} borderWidth  compined right/left border allowance
35253  * @constructor
35254  * @param {String/HTMLElement/Element} el The container element
35255  * @param {Object} config
35256  */
35257 Roo.tree.ColumnTree =  function(el, config)
35258 {
35259    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35260    this.addEvents({
35261         /**
35262         * @event resize
35263         * Fire this event on a container when it resizes
35264         * @param {int} w Width
35265         * @param {int} h Height
35266         */
35267        "resize" : true
35268     });
35269     this.on('resize', this.onResize, this);
35270 };
35271
35272 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35273     //lines:false,
35274     
35275     
35276     borderWidth: Roo.isBorderBox ? 0 : 2, 
35277     headEls : false,
35278     
35279     render : function(){
35280         // add the header.....
35281        
35282         Roo.tree.ColumnTree.superclass.render.apply(this);
35283         
35284         this.el.addClass('x-column-tree');
35285         
35286         this.headers = this.el.createChild(
35287             {cls:'x-tree-headers'},this.innerCt.dom);
35288    
35289         var cols = this.columns, c;
35290         var totalWidth = 0;
35291         this.headEls = [];
35292         var  len = cols.length;
35293         for(var i = 0; i < len; i++){
35294              c = cols[i];
35295              totalWidth += c.width;
35296             this.headEls.push(this.headers.createChild({
35297                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35298                  cn: {
35299                      cls:'x-tree-hd-text',
35300                      html: c.header
35301                  },
35302                  style:'width:'+(c.width-this.borderWidth)+'px;'
35303              }));
35304         }
35305         this.headers.createChild({cls:'x-clear'});
35306         // prevent floats from wrapping when clipped
35307         this.headers.setWidth(totalWidth);
35308         //this.innerCt.setWidth(totalWidth);
35309         this.innerCt.setStyle({ overflow: 'auto' });
35310         this.onResize(this.width, this.height);
35311              
35312         
35313     },
35314     onResize : function(w,h)
35315     {
35316         this.height = h;
35317         this.width = w;
35318         // resize cols..
35319         this.innerCt.setWidth(this.width);
35320         this.innerCt.setHeight(this.height-20);
35321         
35322         // headers...
35323         var cols = this.columns, c;
35324         var totalWidth = 0;
35325         var expEl = false;
35326         var len = cols.length;
35327         for(var i = 0; i < len; i++){
35328             c = cols[i];
35329             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35330                 // it's the expander..
35331                 expEl  = this.headEls[i];
35332                 continue;
35333             }
35334             totalWidth += c.width;
35335             
35336         }
35337         if (expEl) {
35338             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35339         }
35340         this.headers.setWidth(w-20);
35341
35342         
35343         
35344         
35345     }
35346 });
35347 /*
35348  * Based on:
35349  * Ext JS Library 1.1.1
35350  * Copyright(c) 2006-2007, Ext JS, LLC.
35351  *
35352  * Originally Released Under LGPL - original licence link has changed is not relivant.
35353  *
35354  * Fork - LGPL
35355  * <script type="text/javascript">
35356  */
35357  
35358 /**
35359  * @class Roo.menu.Menu
35360  * @extends Roo.util.Observable
35361  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35362  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35363  * @constructor
35364  * Creates a new Menu
35365  * @param {Object} config Configuration options
35366  */
35367 Roo.menu.Menu = function(config){
35368     Roo.apply(this, config);
35369     this.id = this.id || Roo.id();
35370     this.addEvents({
35371         /**
35372          * @event beforeshow
35373          * Fires before this menu is displayed
35374          * @param {Roo.menu.Menu} this
35375          */
35376         beforeshow : true,
35377         /**
35378          * @event beforehide
35379          * Fires before this menu is hidden
35380          * @param {Roo.menu.Menu} this
35381          */
35382         beforehide : true,
35383         /**
35384          * @event show
35385          * Fires after this menu is displayed
35386          * @param {Roo.menu.Menu} this
35387          */
35388         show : true,
35389         /**
35390          * @event hide
35391          * Fires after this menu is hidden
35392          * @param {Roo.menu.Menu} this
35393          */
35394         hide : true,
35395         /**
35396          * @event click
35397          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35398          * @param {Roo.menu.Menu} this
35399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35400          * @param {Roo.EventObject} e
35401          */
35402         click : true,
35403         /**
35404          * @event mouseover
35405          * Fires when the mouse is hovering over this menu
35406          * @param {Roo.menu.Menu} this
35407          * @param {Roo.EventObject} e
35408          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35409          */
35410         mouseover : true,
35411         /**
35412          * @event mouseout
35413          * Fires when the mouse exits this menu
35414          * @param {Roo.menu.Menu} this
35415          * @param {Roo.EventObject} e
35416          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35417          */
35418         mouseout : true,
35419         /**
35420          * @event itemclick
35421          * Fires when a menu item contained in this menu is clicked
35422          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35423          * @param {Roo.EventObject} e
35424          */
35425         itemclick: true
35426     });
35427     if (this.registerMenu) {
35428         Roo.menu.MenuMgr.register(this);
35429     }
35430     
35431     var mis = this.items;
35432     this.items = new Roo.util.MixedCollection();
35433     if(mis){
35434         this.add.apply(this, mis);
35435     }
35436 };
35437
35438 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35439     /**
35440      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35441      */
35442     minWidth : 120,
35443     /**
35444      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35445      * for bottom-right shadow (defaults to "sides")
35446      */
35447     shadow : "sides",
35448     /**
35449      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35450      * this menu (defaults to "tl-tr?")
35451      */
35452     subMenuAlign : "tl-tr?",
35453     /**
35454      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35455      * relative to its element of origin (defaults to "tl-bl?")
35456      */
35457     defaultAlign : "tl-bl?",
35458     /**
35459      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35460      */
35461     allowOtherMenus : false,
35462     /**
35463      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35464      */
35465     registerMenu : true,
35466
35467     hidden:true,
35468
35469     // private
35470     render : function(){
35471         if(this.el){
35472             return;
35473         }
35474         var el = this.el = new Roo.Layer({
35475             cls: "x-menu",
35476             shadow:this.shadow,
35477             constrain: false,
35478             parentEl: this.parentEl || document.body,
35479             zindex:15000
35480         });
35481
35482         this.keyNav = new Roo.menu.MenuNav(this);
35483
35484         if(this.plain){
35485             el.addClass("x-menu-plain");
35486         }
35487         if(this.cls){
35488             el.addClass(this.cls);
35489         }
35490         // generic focus element
35491         this.focusEl = el.createChild({
35492             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35493         });
35494         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35495         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35496         
35497         ul.on("mouseover", this.onMouseOver, this);
35498         ul.on("mouseout", this.onMouseOut, this);
35499         this.items.each(function(item){
35500             if (item.hidden) {
35501                 return;
35502             }
35503             
35504             var li = document.createElement("li");
35505             li.className = "x-menu-list-item";
35506             ul.dom.appendChild(li);
35507             item.render(li, this);
35508         }, this);
35509         this.ul = ul;
35510         this.autoWidth();
35511     },
35512
35513     // private
35514     autoWidth : function(){
35515         var el = this.el, ul = this.ul;
35516         if(!el){
35517             return;
35518         }
35519         var w = this.width;
35520         if(w){
35521             el.setWidth(w);
35522         }else if(Roo.isIE){
35523             el.setWidth(this.minWidth);
35524             var t = el.dom.offsetWidth; // force recalc
35525             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35526         }
35527     },
35528
35529     // private
35530     delayAutoWidth : function(){
35531         if(this.rendered){
35532             if(!this.awTask){
35533                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35534             }
35535             this.awTask.delay(20);
35536         }
35537     },
35538
35539     // private
35540     findTargetItem : function(e){
35541         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35542         if(t && t.menuItemId){
35543             return this.items.get(t.menuItemId);
35544         }
35545     },
35546
35547     // private
35548     onClick : function(e){
35549         Roo.log("menu.onClick");
35550         var t = this.findTargetItem(e);
35551         if(!t){
35552             return;
35553         }
35554         Roo.log(e);
35555         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35556             if(t == this.activeItem && t.shouldDeactivate(e)){
35557                 this.activeItem.deactivate();
35558                 delete this.activeItem;
35559                 return;
35560             }
35561             if(t.canActivate){
35562                 this.setActiveItem(t, true);
35563             }
35564             return;
35565             
35566             
35567         }
35568         
35569         t.onClick(e);
35570         this.fireEvent("click", this, t, e);
35571     },
35572
35573     // private
35574     setActiveItem : function(item, autoExpand){
35575         if(item != this.activeItem){
35576             if(this.activeItem){
35577                 this.activeItem.deactivate();
35578             }
35579             this.activeItem = item;
35580             item.activate(autoExpand);
35581         }else if(autoExpand){
35582             item.expandMenu();
35583         }
35584     },
35585
35586     // private
35587     tryActivate : function(start, step){
35588         var items = this.items;
35589         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35590             var item = items.get(i);
35591             if(!item.disabled && item.canActivate){
35592                 this.setActiveItem(item, false);
35593                 return item;
35594             }
35595         }
35596         return false;
35597     },
35598
35599     // private
35600     onMouseOver : function(e){
35601         var t;
35602         if(t = this.findTargetItem(e)){
35603             if(t.canActivate && !t.disabled){
35604                 this.setActiveItem(t, true);
35605             }
35606         }
35607         this.fireEvent("mouseover", this, e, t);
35608     },
35609
35610     // private
35611     onMouseOut : function(e){
35612         var t;
35613         if(t = this.findTargetItem(e)){
35614             if(t == this.activeItem && t.shouldDeactivate(e)){
35615                 this.activeItem.deactivate();
35616                 delete this.activeItem;
35617             }
35618         }
35619         this.fireEvent("mouseout", this, e, t);
35620     },
35621
35622     /**
35623      * Read-only.  Returns true if the menu is currently displayed, else false.
35624      * @type Boolean
35625      */
35626     isVisible : function(){
35627         return this.el && !this.hidden;
35628     },
35629
35630     /**
35631      * Displays this menu relative to another element
35632      * @param {String/HTMLElement/Roo.Element} element The element to align to
35633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35634      * the element (defaults to this.defaultAlign)
35635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35636      */
35637     show : function(el, pos, parentMenu){
35638         this.parentMenu = parentMenu;
35639         if(!this.el){
35640             this.render();
35641         }
35642         this.fireEvent("beforeshow", this);
35643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35644     },
35645
35646     /**
35647      * Displays this menu at a specific xy position
35648      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35649      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35650      */
35651     showAt : function(xy, parentMenu, /* private: */_e){
35652         this.parentMenu = parentMenu;
35653         if(!this.el){
35654             this.render();
35655         }
35656         if(_e !== false){
35657             this.fireEvent("beforeshow", this);
35658             xy = this.el.adjustForConstraints(xy);
35659         }
35660         this.el.setXY(xy);
35661         this.el.show();
35662         this.hidden = false;
35663         this.focus();
35664         this.fireEvent("show", this);
35665     },
35666
35667     focus : function(){
35668         if(!this.hidden){
35669             this.doFocus.defer(50, this);
35670         }
35671     },
35672
35673     doFocus : function(){
35674         if(!this.hidden){
35675             this.focusEl.focus();
35676         }
35677     },
35678
35679     /**
35680      * Hides this menu and optionally all parent menus
35681      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35682      */
35683     hide : function(deep){
35684         if(this.el && this.isVisible()){
35685             this.fireEvent("beforehide", this);
35686             if(this.activeItem){
35687                 this.activeItem.deactivate();
35688                 this.activeItem = null;
35689             }
35690             this.el.hide();
35691             this.hidden = true;
35692             this.fireEvent("hide", this);
35693         }
35694         if(deep === true && this.parentMenu){
35695             this.parentMenu.hide(true);
35696         }
35697     },
35698
35699     /**
35700      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35701      * Any of the following are valid:
35702      * <ul>
35703      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35704      * <li>An HTMLElement object which will be converted to a menu item</li>
35705      * <li>A menu item config object that will be created as a new menu item</li>
35706      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35707      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35708      * </ul>
35709      * Usage:
35710      * <pre><code>
35711 // Create the menu
35712 var menu = new Roo.menu.Menu();
35713
35714 // Create a menu item to add by reference
35715 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35716
35717 // Add a bunch of items at once using different methods.
35718 // Only the last item added will be returned.
35719 var item = menu.add(
35720     menuItem,                // add existing item by ref
35721     'Dynamic Item',          // new TextItem
35722     '-',                     // new separator
35723     { text: 'Config Item' }  // new item by config
35724 );
35725 </code></pre>
35726      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35727      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35728      */
35729     add : function(){
35730         var a = arguments, l = a.length, item;
35731         for(var i = 0; i < l; i++){
35732             var el = a[i];
35733             if ((typeof(el) == "object") && el.xtype && el.xns) {
35734                 el = Roo.factory(el, Roo.menu);
35735             }
35736             
35737             if(el.render){ // some kind of Item
35738                 item = this.addItem(el);
35739             }else if(typeof el == "string"){ // string
35740                 if(el == "separator" || el == "-"){
35741                     item = this.addSeparator();
35742                 }else{
35743                     item = this.addText(el);
35744                 }
35745             }else if(el.tagName || el.el){ // element
35746                 item = this.addElement(el);
35747             }else if(typeof el == "object"){ // must be menu item config?
35748                 item = this.addMenuItem(el);
35749             }
35750         }
35751         return item;
35752     },
35753
35754     /**
35755      * Returns this menu's underlying {@link Roo.Element} object
35756      * @return {Roo.Element} The element
35757      */
35758     getEl : function(){
35759         if(!this.el){
35760             this.render();
35761         }
35762         return this.el;
35763     },
35764
35765     /**
35766      * Adds a separator bar to the menu
35767      * @return {Roo.menu.Item} The menu item that was added
35768      */
35769     addSeparator : function(){
35770         return this.addItem(new Roo.menu.Separator());
35771     },
35772
35773     /**
35774      * Adds an {@link Roo.Element} object to the menu
35775      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35776      * @return {Roo.menu.Item} The menu item that was added
35777      */
35778     addElement : function(el){
35779         return this.addItem(new Roo.menu.BaseItem(el));
35780     },
35781
35782     /**
35783      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35784      * @param {Roo.menu.Item} item The menu item to add
35785      * @return {Roo.menu.Item} The menu item that was added
35786      */
35787     addItem : function(item){
35788         this.items.add(item);
35789         if(this.ul){
35790             var li = document.createElement("li");
35791             li.className = "x-menu-list-item";
35792             this.ul.dom.appendChild(li);
35793             item.render(li, this);
35794             this.delayAutoWidth();
35795         }
35796         return item;
35797     },
35798
35799     /**
35800      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35801      * @param {Object} config A MenuItem config object
35802      * @return {Roo.menu.Item} The menu item that was added
35803      */
35804     addMenuItem : function(config){
35805         if(!(config instanceof Roo.menu.Item)){
35806             if(typeof config.checked == "boolean"){ // must be check menu item config?
35807                 config = new Roo.menu.CheckItem(config);
35808             }else{
35809                 config = new Roo.menu.Item(config);
35810             }
35811         }
35812         return this.addItem(config);
35813     },
35814
35815     /**
35816      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35817      * @param {String} text The text to display in the menu item
35818      * @return {Roo.menu.Item} The menu item that was added
35819      */
35820     addText : function(text){
35821         return this.addItem(new Roo.menu.TextItem({ text : text }));
35822     },
35823
35824     /**
35825      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35826      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35827      * @param {Roo.menu.Item} item The menu item to add
35828      * @return {Roo.menu.Item} The menu item that was added
35829      */
35830     insert : function(index, item){
35831         this.items.insert(index, item);
35832         if(this.ul){
35833             var li = document.createElement("li");
35834             li.className = "x-menu-list-item";
35835             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35836             item.render(li, this);
35837             this.delayAutoWidth();
35838         }
35839         return item;
35840     },
35841
35842     /**
35843      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35844      * @param {Roo.menu.Item} item The menu item to remove
35845      */
35846     remove : function(item){
35847         this.items.removeKey(item.id);
35848         item.destroy();
35849     },
35850
35851     /**
35852      * Removes and destroys all items in the menu
35853      */
35854     removeAll : function(){
35855         var f;
35856         while(f = this.items.first()){
35857             this.remove(f);
35858         }
35859     }
35860 });
35861
35862 // MenuNav is a private utility class used internally by the Menu
35863 Roo.menu.MenuNav = function(menu){
35864     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35865     this.scope = this.menu = menu;
35866 };
35867
35868 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35869     doRelay : function(e, h){
35870         var k = e.getKey();
35871         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35872             this.menu.tryActivate(0, 1);
35873             return false;
35874         }
35875         return h.call(this.scope || this, e, this.menu);
35876     },
35877
35878     up : function(e, m){
35879         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35880             m.tryActivate(m.items.length-1, -1);
35881         }
35882     },
35883
35884     down : function(e, m){
35885         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35886             m.tryActivate(0, 1);
35887         }
35888     },
35889
35890     right : function(e, m){
35891         if(m.activeItem){
35892             m.activeItem.expandMenu(true);
35893         }
35894     },
35895
35896     left : function(e, m){
35897         m.hide();
35898         if(m.parentMenu && m.parentMenu.activeItem){
35899             m.parentMenu.activeItem.activate();
35900         }
35901     },
35902
35903     enter : function(e, m){
35904         if(m.activeItem){
35905             e.stopPropagation();
35906             m.activeItem.onClick(e);
35907             m.fireEvent("click", this, m.activeItem);
35908             return true;
35909         }
35910     }
35911 });/*
35912  * Based on:
35913  * Ext JS Library 1.1.1
35914  * Copyright(c) 2006-2007, Ext JS, LLC.
35915  *
35916  * Originally Released Under LGPL - original licence link has changed is not relivant.
35917  *
35918  * Fork - LGPL
35919  * <script type="text/javascript">
35920  */
35921  
35922 /**
35923  * @class Roo.menu.MenuMgr
35924  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35925  * @singleton
35926  */
35927 Roo.menu.MenuMgr = function(){
35928    var menus, active, groups = {}, attached = false, lastShow = new Date();
35929
35930    // private - called when first menu is created
35931    function init(){
35932        menus = {};
35933        active = new Roo.util.MixedCollection();
35934        Roo.get(document).addKeyListener(27, function(){
35935            if(active.length > 0){
35936                hideAll();
35937            }
35938        });
35939    }
35940
35941    // private
35942    function hideAll(){
35943        if(active && active.length > 0){
35944            var c = active.clone();
35945            c.each(function(m){
35946                m.hide();
35947            });
35948        }
35949    }
35950
35951    // private
35952    function onHide(m){
35953        active.remove(m);
35954        if(active.length < 1){
35955            Roo.get(document).un("mousedown", onMouseDown);
35956            attached = false;
35957        }
35958    }
35959
35960    // private
35961    function onShow(m){
35962        var last = active.last();
35963        lastShow = new Date();
35964        active.add(m);
35965        if(!attached){
35966            Roo.get(document).on("mousedown", onMouseDown);
35967            attached = true;
35968        }
35969        if(m.parentMenu){
35970           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35971           m.parentMenu.activeChild = m;
35972        }else if(last && last.isVisible()){
35973           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35974        }
35975    }
35976
35977    // private
35978    function onBeforeHide(m){
35979        if(m.activeChild){
35980            m.activeChild.hide();
35981        }
35982        if(m.autoHideTimer){
35983            clearTimeout(m.autoHideTimer);
35984            delete m.autoHideTimer;
35985        }
35986    }
35987
35988    // private
35989    function onBeforeShow(m){
35990        var pm = m.parentMenu;
35991        if(!pm && !m.allowOtherMenus){
35992            hideAll();
35993        }else if(pm && pm.activeChild && active != m){
35994            pm.activeChild.hide();
35995        }
35996    }
35997
35998    // private
35999    function onMouseDown(e){
36000        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36001            hideAll();
36002        }
36003    }
36004
36005    // private
36006    function onBeforeCheck(mi, state){
36007        if(state){
36008            var g = groups[mi.group];
36009            for(var i = 0, l = g.length; i < l; i++){
36010                if(g[i] != mi){
36011                    g[i].setChecked(false);
36012                }
36013            }
36014        }
36015    }
36016
36017    return {
36018
36019        /**
36020         * Hides all menus that are currently visible
36021         */
36022        hideAll : function(){
36023             hideAll();  
36024        },
36025
36026        // private
36027        register : function(menu){
36028            if(!menus){
36029                init();
36030            }
36031            menus[menu.id] = menu;
36032            menu.on("beforehide", onBeforeHide);
36033            menu.on("hide", onHide);
36034            menu.on("beforeshow", onBeforeShow);
36035            menu.on("show", onShow);
36036            var g = menu.group;
36037            if(g && menu.events["checkchange"]){
36038                if(!groups[g]){
36039                    groups[g] = [];
36040                }
36041                groups[g].push(menu);
36042                menu.on("checkchange", onCheck);
36043            }
36044        },
36045
36046         /**
36047          * Returns a {@link Roo.menu.Menu} object
36048          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36049          * be used to generate and return a new Menu instance.
36050          */
36051        get : function(menu){
36052            if(typeof menu == "string"){ // menu id
36053                return menus[menu];
36054            }else if(menu.events){  // menu instance
36055                return menu;
36056            }else if(typeof menu.length == 'number'){ // array of menu items?
36057                return new Roo.menu.Menu({items:menu});
36058            }else{ // otherwise, must be a config
36059                return new Roo.menu.Menu(menu);
36060            }
36061        },
36062
36063        // private
36064        unregister : function(menu){
36065            delete menus[menu.id];
36066            menu.un("beforehide", onBeforeHide);
36067            menu.un("hide", onHide);
36068            menu.un("beforeshow", onBeforeShow);
36069            menu.un("show", onShow);
36070            var g = menu.group;
36071            if(g && menu.events["checkchange"]){
36072                groups[g].remove(menu);
36073                menu.un("checkchange", onCheck);
36074            }
36075        },
36076
36077        // private
36078        registerCheckable : function(menuItem){
36079            var g = menuItem.group;
36080            if(g){
36081                if(!groups[g]){
36082                    groups[g] = [];
36083                }
36084                groups[g].push(menuItem);
36085                menuItem.on("beforecheckchange", onBeforeCheck);
36086            }
36087        },
36088
36089        // private
36090        unregisterCheckable : function(menuItem){
36091            var g = menuItem.group;
36092            if(g){
36093                groups[g].remove(menuItem);
36094                menuItem.un("beforecheckchange", onBeforeCheck);
36095            }
36096        }
36097    };
36098 }();/*
36099  * Based on:
36100  * Ext JS Library 1.1.1
36101  * Copyright(c) 2006-2007, Ext JS, LLC.
36102  *
36103  * Originally Released Under LGPL - original licence link has changed is not relivant.
36104  *
36105  * Fork - LGPL
36106  * <script type="text/javascript">
36107  */
36108  
36109
36110 /**
36111  * @class Roo.menu.BaseItem
36112  * @extends Roo.Component
36113  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36114  * management and base configuration options shared by all menu components.
36115  * @constructor
36116  * Creates a new BaseItem
36117  * @param {Object} config Configuration options
36118  */
36119 Roo.menu.BaseItem = function(config){
36120     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36121
36122     this.addEvents({
36123         /**
36124          * @event click
36125          * Fires when this item is clicked
36126          * @param {Roo.menu.BaseItem} this
36127          * @param {Roo.EventObject} e
36128          */
36129         click: true,
36130         /**
36131          * @event activate
36132          * Fires when this item is activated
36133          * @param {Roo.menu.BaseItem} this
36134          */
36135         activate : true,
36136         /**
36137          * @event deactivate
36138          * Fires when this item is deactivated
36139          * @param {Roo.menu.BaseItem} this
36140          */
36141         deactivate : true
36142     });
36143
36144     if(this.handler){
36145         this.on("click", this.handler, this.scope, true);
36146     }
36147 };
36148
36149 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36150     /**
36151      * @cfg {Function} handler
36152      * A function that will handle the click event of this menu item (defaults to undefined)
36153      */
36154     /**
36155      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36156      */
36157     canActivate : false,
36158     
36159      /**
36160      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36161      */
36162     hidden: false,
36163     
36164     /**
36165      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36166      */
36167     activeClass : "x-menu-item-active",
36168     /**
36169      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36170      */
36171     hideOnClick : true,
36172     /**
36173      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36174      */
36175     hideDelay : 100,
36176
36177     // private
36178     ctype: "Roo.menu.BaseItem",
36179
36180     // private
36181     actionMode : "container",
36182
36183     // private
36184     render : function(container, parentMenu){
36185         this.parentMenu = parentMenu;
36186         Roo.menu.BaseItem.superclass.render.call(this, container);
36187         this.container.menuItemId = this.id;
36188     },
36189
36190     // private
36191     onRender : function(container, position){
36192         this.el = Roo.get(this.el);
36193         container.dom.appendChild(this.el.dom);
36194     },
36195
36196     // private
36197     onClick : function(e){
36198         if(!this.disabled && this.fireEvent("click", this, e) !== false
36199                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36200             this.handleClick(e);
36201         }else{
36202             e.stopEvent();
36203         }
36204     },
36205
36206     // private
36207     activate : function(){
36208         if(this.disabled){
36209             return false;
36210         }
36211         var li = this.container;
36212         li.addClass(this.activeClass);
36213         this.region = li.getRegion().adjust(2, 2, -2, -2);
36214         this.fireEvent("activate", this);
36215         return true;
36216     },
36217
36218     // private
36219     deactivate : function(){
36220         this.container.removeClass(this.activeClass);
36221         this.fireEvent("deactivate", this);
36222     },
36223
36224     // private
36225     shouldDeactivate : function(e){
36226         return !this.region || !this.region.contains(e.getPoint());
36227     },
36228
36229     // private
36230     handleClick : function(e){
36231         if(this.hideOnClick){
36232             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36233         }
36234     },
36235
36236     // private
36237     expandMenu : function(autoActivate){
36238         // do nothing
36239     },
36240
36241     // private
36242     hideMenu : function(){
36243         // do nothing
36244     }
36245 });/*
36246  * Based on:
36247  * Ext JS Library 1.1.1
36248  * Copyright(c) 2006-2007, Ext JS, LLC.
36249  *
36250  * Originally Released Under LGPL - original licence link has changed is not relivant.
36251  *
36252  * Fork - LGPL
36253  * <script type="text/javascript">
36254  */
36255  
36256 /**
36257  * @class Roo.menu.Adapter
36258  * @extends Roo.menu.BaseItem
36259  * 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.
36260  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36261  * @constructor
36262  * Creates a new Adapter
36263  * @param {Object} config Configuration options
36264  */
36265 Roo.menu.Adapter = function(component, config){
36266     Roo.menu.Adapter.superclass.constructor.call(this, config);
36267     this.component = component;
36268 };
36269 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36270     // private
36271     canActivate : true,
36272
36273     // private
36274     onRender : function(container, position){
36275         this.component.render(container);
36276         this.el = this.component.getEl();
36277     },
36278
36279     // private
36280     activate : function(){
36281         if(this.disabled){
36282             return false;
36283         }
36284         this.component.focus();
36285         this.fireEvent("activate", this);
36286         return true;
36287     },
36288
36289     // private
36290     deactivate : function(){
36291         this.fireEvent("deactivate", this);
36292     },
36293
36294     // private
36295     disable : function(){
36296         this.component.disable();
36297         Roo.menu.Adapter.superclass.disable.call(this);
36298     },
36299
36300     // private
36301     enable : function(){
36302         this.component.enable();
36303         Roo.menu.Adapter.superclass.enable.call(this);
36304     }
36305 });/*
36306  * Based on:
36307  * Ext JS Library 1.1.1
36308  * Copyright(c) 2006-2007, Ext JS, LLC.
36309  *
36310  * Originally Released Under LGPL - original licence link has changed is not relivant.
36311  *
36312  * Fork - LGPL
36313  * <script type="text/javascript">
36314  */
36315
36316 /**
36317  * @class Roo.menu.TextItem
36318  * @extends Roo.menu.BaseItem
36319  * Adds a static text string to a menu, usually used as either a heading or group separator.
36320  * Note: old style constructor with text is still supported.
36321  * 
36322  * @constructor
36323  * Creates a new TextItem
36324  * @param {Object} cfg Configuration
36325  */
36326 Roo.menu.TextItem = function(cfg){
36327     if (typeof(cfg) == 'string') {
36328         this.text = cfg;
36329     } else {
36330         Roo.apply(this,cfg);
36331     }
36332     
36333     Roo.menu.TextItem.superclass.constructor.call(this);
36334 };
36335
36336 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36337     /**
36338      * @cfg {Boolean} text Text to show on item.
36339      */
36340     text : '',
36341     
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      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36348      */
36349     itemCls : "x-menu-text",
36350
36351     // private
36352     onRender : function(){
36353         var s = document.createElement("span");
36354         s.className = this.itemCls;
36355         s.innerHTML = this.text;
36356         this.el = s;
36357         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36358     }
36359 });/*
36360  * Based on:
36361  * Ext JS Library 1.1.1
36362  * Copyright(c) 2006-2007, Ext JS, LLC.
36363  *
36364  * Originally Released Under LGPL - original licence link has changed is not relivant.
36365  *
36366  * Fork - LGPL
36367  * <script type="text/javascript">
36368  */
36369
36370 /**
36371  * @class Roo.menu.Separator
36372  * @extends Roo.menu.BaseItem
36373  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36374  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36375  * @constructor
36376  * @param {Object} config Configuration options
36377  */
36378 Roo.menu.Separator = function(config){
36379     Roo.menu.Separator.superclass.constructor.call(this, config);
36380 };
36381
36382 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36383     /**
36384      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36385      */
36386     itemCls : "x-menu-sep",
36387     /**
36388      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36389      */
36390     hideOnClick : false,
36391
36392     // private
36393     onRender : function(li){
36394         var s = document.createElement("span");
36395         s.className = this.itemCls;
36396         s.innerHTML = "&#160;";
36397         this.el = s;
36398         li.addClass("x-menu-sep-li");
36399         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36400     }
36401 });/*
36402  * Based on:
36403  * Ext JS Library 1.1.1
36404  * Copyright(c) 2006-2007, Ext JS, LLC.
36405  *
36406  * Originally Released Under LGPL - original licence link has changed is not relivant.
36407  *
36408  * Fork - LGPL
36409  * <script type="text/javascript">
36410  */
36411 /**
36412  * @class Roo.menu.Item
36413  * @extends Roo.menu.BaseItem
36414  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36415  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36416  * activation and click handling.
36417  * @constructor
36418  * Creates a new Item
36419  * @param {Object} config Configuration options
36420  */
36421 Roo.menu.Item = function(config){
36422     Roo.menu.Item.superclass.constructor.call(this, config);
36423     if(this.menu){
36424         this.menu = Roo.menu.MenuMgr.get(this.menu);
36425     }
36426 };
36427 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36428     
36429     /**
36430      * @cfg {String} text
36431      * The text to show on the menu item.
36432      */
36433     text: '',
36434      /**
36435      * @cfg {String} HTML to render in menu
36436      * The text to show on the menu item (HTML version).
36437      */
36438     html: '',
36439     /**
36440      * @cfg {String} icon
36441      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36442      */
36443     icon: undefined,
36444     /**
36445      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36446      */
36447     itemCls : "x-menu-item",
36448     /**
36449      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36450      */
36451     canActivate : true,
36452     /**
36453      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36454      */
36455     showDelay: 200,
36456     // doc'd in BaseItem
36457     hideDelay: 200,
36458
36459     // private
36460     ctype: "Roo.menu.Item",
36461     
36462     // private
36463     onRender : function(container, position){
36464         var el = document.createElement("a");
36465         el.hideFocus = true;
36466         el.unselectable = "on";
36467         el.href = this.href || "#";
36468         if(this.hrefTarget){
36469             el.target = this.hrefTarget;
36470         }
36471         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36472         
36473         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36474         
36475         el.innerHTML = String.format(
36476                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36477                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36478         this.el = el;
36479         Roo.menu.Item.superclass.onRender.call(this, container, position);
36480     },
36481
36482     /**
36483      * Sets the text to display in this menu item
36484      * @param {String} text The text to display
36485      * @param {Boolean} isHTML true to indicate text is pure html.
36486      */
36487     setText : function(text, isHTML){
36488         if (isHTML) {
36489             this.html = text;
36490         } else {
36491             this.text = text;
36492             this.html = '';
36493         }
36494         if(this.rendered){
36495             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36496      
36497             this.el.update(String.format(
36498                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36499                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36500             this.parentMenu.autoWidth();
36501         }
36502     },
36503
36504     // private
36505     handleClick : function(e){
36506         if(!this.href){ // if no link defined, stop the event automatically
36507             e.stopEvent();
36508         }
36509         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36510     },
36511
36512     // private
36513     activate : function(autoExpand){
36514         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36515             this.focus();
36516             if(autoExpand){
36517                 this.expandMenu();
36518             }
36519         }
36520         return true;
36521     },
36522
36523     // private
36524     shouldDeactivate : function(e){
36525         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36526             if(this.menu && this.menu.isVisible()){
36527                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36528             }
36529             return true;
36530         }
36531         return false;
36532     },
36533
36534     // private
36535     deactivate : function(){
36536         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36537         this.hideMenu();
36538     },
36539
36540     // private
36541     expandMenu : function(autoActivate){
36542         if(!this.disabled && this.menu){
36543             clearTimeout(this.hideTimer);
36544             delete this.hideTimer;
36545             if(!this.menu.isVisible() && !this.showTimer){
36546                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36547             }else if (this.menu.isVisible() && autoActivate){
36548                 this.menu.tryActivate(0, 1);
36549             }
36550         }
36551     },
36552
36553     // private
36554     deferExpand : function(autoActivate){
36555         delete this.showTimer;
36556         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36557         if(autoActivate){
36558             this.menu.tryActivate(0, 1);
36559         }
36560     },
36561
36562     // private
36563     hideMenu : function(){
36564         clearTimeout(this.showTimer);
36565         delete this.showTimer;
36566         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36567             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36568         }
36569     },
36570
36571     // private
36572     deferHide : function(){
36573         delete this.hideTimer;
36574         this.menu.hide();
36575     }
36576 });/*
36577  * Based on:
36578  * Ext JS Library 1.1.1
36579  * Copyright(c) 2006-2007, Ext JS, LLC.
36580  *
36581  * Originally Released Under LGPL - original licence link has changed is not relivant.
36582  *
36583  * Fork - LGPL
36584  * <script type="text/javascript">
36585  */
36586  
36587 /**
36588  * @class Roo.menu.CheckItem
36589  * @extends Roo.menu.Item
36590  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36591  * @constructor
36592  * Creates a new CheckItem
36593  * @param {Object} config Configuration options
36594  */
36595 Roo.menu.CheckItem = function(config){
36596     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36597     this.addEvents({
36598         /**
36599          * @event beforecheckchange
36600          * Fires before the checked value is set, providing an opportunity to cancel if needed
36601          * @param {Roo.menu.CheckItem} this
36602          * @param {Boolean} checked The new checked value that will be set
36603          */
36604         "beforecheckchange" : true,
36605         /**
36606          * @event checkchange
36607          * Fires after the checked value has been set
36608          * @param {Roo.menu.CheckItem} this
36609          * @param {Boolean} checked The checked value that was set
36610          */
36611         "checkchange" : true
36612     });
36613     if(this.checkHandler){
36614         this.on('checkchange', this.checkHandler, this.scope);
36615     }
36616 };
36617 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36618     /**
36619      * @cfg {String} group
36620      * All check items with the same group name will automatically be grouped into a single-select
36621      * radio button group (defaults to '')
36622      */
36623     /**
36624      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36625      */
36626     itemCls : "x-menu-item x-menu-check-item",
36627     /**
36628      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36629      */
36630     groupClass : "x-menu-group-item",
36631
36632     /**
36633      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36634      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36635      * initialized with checked = true will be rendered as checked.
36636      */
36637     checked: false,
36638
36639     // private
36640     ctype: "Roo.menu.CheckItem",
36641
36642     // private
36643     onRender : function(c){
36644         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36645         if(this.group){
36646             this.el.addClass(this.groupClass);
36647         }
36648         Roo.menu.MenuMgr.registerCheckable(this);
36649         if(this.checked){
36650             this.checked = false;
36651             this.setChecked(true, true);
36652         }
36653     },
36654
36655     // private
36656     destroy : function(){
36657         if(this.rendered){
36658             Roo.menu.MenuMgr.unregisterCheckable(this);
36659         }
36660         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36661     },
36662
36663     /**
36664      * Set the checked state of this item
36665      * @param {Boolean} checked The new checked value
36666      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36667      */
36668     setChecked : function(state, suppressEvent){
36669         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36670             if(this.container){
36671                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36672             }
36673             this.checked = state;
36674             if(suppressEvent !== true){
36675                 this.fireEvent("checkchange", this, state);
36676             }
36677         }
36678     },
36679
36680     // private
36681     handleClick : function(e){
36682        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36683            this.setChecked(!this.checked);
36684        }
36685        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36686     }
36687 });/*
36688  * Based on:
36689  * Ext JS Library 1.1.1
36690  * Copyright(c) 2006-2007, Ext JS, LLC.
36691  *
36692  * Originally Released Under LGPL - original licence link has changed is not relivant.
36693  *
36694  * Fork - LGPL
36695  * <script type="text/javascript">
36696  */
36697  
36698 /**
36699  * @class Roo.menu.DateItem
36700  * @extends Roo.menu.Adapter
36701  * A menu item that wraps the {@link Roo.DatPicker} component.
36702  * @constructor
36703  * Creates a new DateItem
36704  * @param {Object} config Configuration options
36705  */
36706 Roo.menu.DateItem = function(config){
36707     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36708     /** The Roo.DatePicker object @type Roo.DatePicker */
36709     this.picker = this.component;
36710     this.addEvents({select: true});
36711     
36712     this.picker.on("render", function(picker){
36713         picker.getEl().swallowEvent("click");
36714         picker.container.addClass("x-menu-date-item");
36715     });
36716
36717     this.picker.on("select", this.onSelect, this);
36718 };
36719
36720 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36721     // private
36722     onSelect : function(picker, date){
36723         this.fireEvent("select", this, date, picker);
36724         Roo.menu.DateItem.superclass.handleClick.call(this);
36725     }
36726 });/*
36727  * Based on:
36728  * Ext JS Library 1.1.1
36729  * Copyright(c) 2006-2007, Ext JS, LLC.
36730  *
36731  * Originally Released Under LGPL - original licence link has changed is not relivant.
36732  *
36733  * Fork - LGPL
36734  * <script type="text/javascript">
36735  */
36736  
36737 /**
36738  * @class Roo.menu.ColorItem
36739  * @extends Roo.menu.Adapter
36740  * A menu item that wraps the {@link Roo.ColorPalette} component.
36741  * @constructor
36742  * Creates a new ColorItem
36743  * @param {Object} config Configuration options
36744  */
36745 Roo.menu.ColorItem = function(config){
36746     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36747     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36748     this.palette = this.component;
36749     this.relayEvents(this.palette, ["select"]);
36750     if(this.selectHandler){
36751         this.on('select', this.selectHandler, this.scope);
36752     }
36753 };
36754 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36755  * Based on:
36756  * Ext JS Library 1.1.1
36757  * Copyright(c) 2006-2007, Ext JS, LLC.
36758  *
36759  * Originally Released Under LGPL - original licence link has changed is not relivant.
36760  *
36761  * Fork - LGPL
36762  * <script type="text/javascript">
36763  */
36764  
36765
36766 /**
36767  * @class Roo.menu.DateMenu
36768  * @extends Roo.menu.Menu
36769  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36770  * @constructor
36771  * Creates a new DateMenu
36772  * @param {Object} config Configuration options
36773  */
36774 Roo.menu.DateMenu = function(config){
36775     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36776     this.plain = true;
36777     var di = new Roo.menu.DateItem(config);
36778     this.add(di);
36779     /**
36780      * The {@link Roo.DatePicker} instance for this DateMenu
36781      * @type DatePicker
36782      */
36783     this.picker = di.picker;
36784     /**
36785      * @event select
36786      * @param {DatePicker} picker
36787      * @param {Date} date
36788      */
36789     this.relayEvents(di, ["select"]);
36790     this.on('beforeshow', function(){
36791         if(this.picker){
36792             this.picker.hideMonthPicker(false);
36793         }
36794     }, this);
36795 };
36796 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36797     cls:'x-date-menu'
36798 });/*
36799  * Based on:
36800  * Ext JS Library 1.1.1
36801  * Copyright(c) 2006-2007, Ext JS, LLC.
36802  *
36803  * Originally Released Under LGPL - original licence link has changed is not relivant.
36804  *
36805  * Fork - LGPL
36806  * <script type="text/javascript">
36807  */
36808  
36809
36810 /**
36811  * @class Roo.menu.ColorMenu
36812  * @extends Roo.menu.Menu
36813  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36814  * @constructor
36815  * Creates a new ColorMenu
36816  * @param {Object} config Configuration options
36817  */
36818 Roo.menu.ColorMenu = function(config){
36819     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36820     this.plain = true;
36821     var ci = new Roo.menu.ColorItem(config);
36822     this.add(ci);
36823     /**
36824      * The {@link Roo.ColorPalette} instance for this ColorMenu
36825      * @type ColorPalette
36826      */
36827     this.palette = ci.palette;
36828     /**
36829      * @event select
36830      * @param {ColorPalette} palette
36831      * @param {String} color
36832      */
36833     this.relayEvents(ci, ["select"]);
36834 };
36835 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36836  * Based on:
36837  * Ext JS Library 1.1.1
36838  * Copyright(c) 2006-2007, Ext JS, LLC.
36839  *
36840  * Originally Released Under LGPL - original licence link has changed is not relivant.
36841  *
36842  * Fork - LGPL
36843  * <script type="text/javascript">
36844  */
36845  
36846 /**
36847  * @class Roo.form.Field
36848  * @extends Roo.BoxComponent
36849  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36850  * @constructor
36851  * Creates a new Field
36852  * @param {Object} config Configuration options
36853  */
36854 Roo.form.Field = function(config){
36855     Roo.form.Field.superclass.constructor.call(this, config);
36856 };
36857
36858 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36859     /**
36860      * @cfg {String} fieldLabel Label to use when rendering a form.
36861      */
36862        /**
36863      * @cfg {String} qtip Mouse over tip
36864      */
36865      
36866     /**
36867      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36868      */
36869     invalidClass : "x-form-invalid",
36870     /**
36871      * @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")
36872      */
36873     invalidText : "The value in this field is invalid",
36874     /**
36875      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36876      */
36877     focusClass : "x-form-focus",
36878     /**
36879      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36880       automatic validation (defaults to "keyup").
36881      */
36882     validationEvent : "keyup",
36883     /**
36884      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36885      */
36886     validateOnBlur : true,
36887     /**
36888      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36889      */
36890     validationDelay : 250,
36891     /**
36892      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36893      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36894      */
36895     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36896     /**
36897      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36898      */
36899     fieldClass : "x-form-field",
36900     /**
36901      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36902      *<pre>
36903 Value         Description
36904 -----------   ----------------------------------------------------------------------
36905 qtip          Display a quick tip when the user hovers over the field
36906 title         Display a default browser title attribute popup
36907 under         Add a block div beneath the field containing the error text
36908 side          Add an error icon to the right of the field with a popup on hover
36909 [element id]  Add the error text directly to the innerHTML of the specified element
36910 </pre>
36911      */
36912     msgTarget : 'qtip',
36913     /**
36914      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36915      */
36916     msgFx : 'normal',
36917
36918     /**
36919      * @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.
36920      */
36921     readOnly : false,
36922
36923     /**
36924      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36925      */
36926     disabled : false,
36927
36928     /**
36929      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36930      */
36931     inputType : undefined,
36932     
36933     /**
36934      * @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).
36935          */
36936         tabIndex : undefined,
36937         
36938     // private
36939     isFormField : true,
36940
36941     // private
36942     hasFocus : false,
36943     /**
36944      * @property {Roo.Element} fieldEl
36945      * Element Containing the rendered Field (with label etc.)
36946      */
36947     /**
36948      * @cfg {Mixed} value A value to initialize this field with.
36949      */
36950     value : undefined,
36951
36952     /**
36953      * @cfg {String} name The field's HTML name attribute.
36954      */
36955     /**
36956      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36957      */
36958
36959         // private ??
36960         initComponent : function(){
36961         Roo.form.Field.superclass.initComponent.call(this);
36962         this.addEvents({
36963             /**
36964              * @event focus
36965              * Fires when this field receives input focus.
36966              * @param {Roo.form.Field} this
36967              */
36968             focus : true,
36969             /**
36970              * @event blur
36971              * Fires when this field loses input focus.
36972              * @param {Roo.form.Field} this
36973              */
36974             blur : true,
36975             /**
36976              * @event specialkey
36977              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36978              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36979              * @param {Roo.form.Field} this
36980              * @param {Roo.EventObject} e The event object
36981              */
36982             specialkey : true,
36983             /**
36984              * @event change
36985              * Fires just before the field blurs if the field value has changed.
36986              * @param {Roo.form.Field} this
36987              * @param {Mixed} newValue The new value
36988              * @param {Mixed} oldValue The original value
36989              */
36990             change : true,
36991             /**
36992              * @event invalid
36993              * Fires after the field has been marked as invalid.
36994              * @param {Roo.form.Field} this
36995              * @param {String} msg The validation message
36996              */
36997             invalid : true,
36998             /**
36999              * @event valid
37000              * Fires after the field has been validated with no errors.
37001              * @param {Roo.form.Field} this
37002              */
37003             valid : true,
37004              /**
37005              * @event keyup
37006              * Fires after the key up
37007              * @param {Roo.form.Field} this
37008              * @param {Roo.EventObject}  e The event Object
37009              */
37010             keyup : true
37011         });
37012     },
37013
37014     /**
37015      * Returns the name attribute of the field if available
37016      * @return {String} name The field name
37017      */
37018     getName: function(){
37019          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37020     },
37021
37022     // private
37023     onRender : function(ct, position){
37024         Roo.form.Field.superclass.onRender.call(this, ct, position);
37025         if(!this.el){
37026             var cfg = this.getAutoCreate();
37027             if(!cfg.name){
37028                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37029             }
37030             if (!cfg.name.length) {
37031                 delete cfg.name;
37032             }
37033             if(this.inputType){
37034                 cfg.type = this.inputType;
37035             }
37036             this.el = ct.createChild(cfg, position);
37037         }
37038         var type = this.el.dom.type;
37039         if(type){
37040             if(type == 'password'){
37041                 type = 'text';
37042             }
37043             this.el.addClass('x-form-'+type);
37044         }
37045         if(this.readOnly){
37046             this.el.dom.readOnly = true;
37047         }
37048         if(this.tabIndex !== undefined){
37049             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37050         }
37051
37052         this.el.addClass([this.fieldClass, this.cls]);
37053         this.initValue();
37054     },
37055
37056     /**
37057      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37058      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37059      * @return {Roo.form.Field} this
37060      */
37061     applyTo : function(target){
37062         this.allowDomMove = false;
37063         this.el = Roo.get(target);
37064         this.render(this.el.dom.parentNode);
37065         return this;
37066     },
37067
37068     // private
37069     initValue : function(){
37070         if(this.value !== undefined){
37071             this.setValue(this.value);
37072         }else if(this.el.dom.value.length > 0){
37073             this.setValue(this.el.dom.value);
37074         }
37075     },
37076
37077     /**
37078      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37079      */
37080     isDirty : function() {
37081         if(this.disabled) {
37082             return false;
37083         }
37084         return String(this.getValue()) !== String(this.originalValue);
37085     },
37086
37087     // private
37088     afterRender : function(){
37089         Roo.form.Field.superclass.afterRender.call(this);
37090         this.initEvents();
37091     },
37092
37093     // private
37094     fireKey : function(e){
37095         //Roo.log('field ' + e.getKey());
37096         if(e.isNavKeyPress()){
37097             this.fireEvent("specialkey", this, e);
37098         }
37099     },
37100
37101     /**
37102      * Resets the current field value to the originally loaded value and clears any validation messages
37103      */
37104     reset : function(){
37105         this.setValue(this.resetValue);
37106         this.clearInvalid();
37107     },
37108
37109     // private
37110     initEvents : function(){
37111         // safari killled keypress - so keydown is now used..
37112         this.el.on("keydown" , this.fireKey,  this);
37113         this.el.on("focus", this.onFocus,  this);
37114         this.el.on("blur", this.onBlur,  this);
37115         this.el.relayEvent('keyup', this);
37116
37117         // reference to original value for reset
37118         this.originalValue = this.getValue();
37119         this.resetValue =  this.getValue();
37120     },
37121
37122     // private
37123     onFocus : function(){
37124         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37125             this.el.addClass(this.focusClass);
37126         }
37127         if(!this.hasFocus){
37128             this.hasFocus = true;
37129             this.startValue = this.getValue();
37130             this.fireEvent("focus", this);
37131         }
37132     },
37133
37134     beforeBlur : Roo.emptyFn,
37135
37136     // private
37137     onBlur : function(){
37138         this.beforeBlur();
37139         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37140             this.el.removeClass(this.focusClass);
37141         }
37142         this.hasFocus = false;
37143         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37144             this.validate();
37145         }
37146         var v = this.getValue();
37147         if(String(v) !== String(this.startValue)){
37148             this.fireEvent('change', this, v, this.startValue);
37149         }
37150         this.fireEvent("blur", this);
37151     },
37152
37153     /**
37154      * Returns whether or not the field value is currently valid
37155      * @param {Boolean} preventMark True to disable marking the field invalid
37156      * @return {Boolean} True if the value is valid, else false
37157      */
37158     isValid : function(preventMark){
37159         if(this.disabled){
37160             return true;
37161         }
37162         var restore = this.preventMark;
37163         this.preventMark = preventMark === true;
37164         var v = this.validateValue(this.processValue(this.getRawValue()));
37165         this.preventMark = restore;
37166         return v;
37167     },
37168
37169     /**
37170      * Validates the field value
37171      * @return {Boolean} True if the value is valid, else false
37172      */
37173     validate : function(){
37174         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37175             this.clearInvalid();
37176             return true;
37177         }
37178         return false;
37179     },
37180
37181     processValue : function(value){
37182         return value;
37183     },
37184
37185     // private
37186     // Subclasses should provide the validation implementation by overriding this
37187     validateValue : function(value){
37188         return true;
37189     },
37190
37191     /**
37192      * Mark this field as invalid
37193      * @param {String} msg The validation message
37194      */
37195     markInvalid : function(msg){
37196         if(!this.rendered || this.preventMark){ // not rendered
37197             return;
37198         }
37199         
37200         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37201         
37202         obj.el.addClass(this.invalidClass);
37203         msg = msg || this.invalidText;
37204         switch(this.msgTarget){
37205             case 'qtip':
37206                 obj.el.dom.qtip = msg;
37207                 obj.el.dom.qclass = 'x-form-invalid-tip';
37208                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37209                     Roo.QuickTips.enable();
37210                 }
37211                 break;
37212             case 'title':
37213                 this.el.dom.title = msg;
37214                 break;
37215             case 'under':
37216                 if(!this.errorEl){
37217                     var elp = this.el.findParent('.x-form-element', 5, true);
37218                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37219                     this.errorEl.setWidth(elp.getWidth(true)-20);
37220                 }
37221                 this.errorEl.update(msg);
37222                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37223                 break;
37224             case 'side':
37225                 if(!this.errorIcon){
37226                     var elp = this.el.findParent('.x-form-element', 5, true);
37227                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37228                 }
37229                 this.alignErrorIcon();
37230                 this.errorIcon.dom.qtip = msg;
37231                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37232                 this.errorIcon.show();
37233                 this.on('resize', this.alignErrorIcon, this);
37234                 break;
37235             default:
37236                 var t = Roo.getDom(this.msgTarget);
37237                 t.innerHTML = msg;
37238                 t.style.display = this.msgDisplay;
37239                 break;
37240         }
37241         this.fireEvent('invalid', this, msg);
37242     },
37243
37244     // private
37245     alignErrorIcon : function(){
37246         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37247     },
37248
37249     /**
37250      * Clear any invalid styles/messages for this field
37251      */
37252     clearInvalid : function(){
37253         if(!this.rendered || this.preventMark){ // not rendered
37254             return;
37255         }
37256         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37257         
37258         obj.el.removeClass(this.invalidClass);
37259         switch(this.msgTarget){
37260             case 'qtip':
37261                 obj.el.dom.qtip = '';
37262                 break;
37263             case 'title':
37264                 this.el.dom.title = '';
37265                 break;
37266             case 'under':
37267                 if(this.errorEl){
37268                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37269                 }
37270                 break;
37271             case 'side':
37272                 if(this.errorIcon){
37273                     this.errorIcon.dom.qtip = '';
37274                     this.errorIcon.hide();
37275                     this.un('resize', this.alignErrorIcon, this);
37276                 }
37277                 break;
37278             default:
37279                 var t = Roo.getDom(this.msgTarget);
37280                 t.innerHTML = '';
37281                 t.style.display = 'none';
37282                 break;
37283         }
37284         this.fireEvent('valid', this);
37285     },
37286
37287     /**
37288      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37289      * @return {Mixed} value The field value
37290      */
37291     getRawValue : function(){
37292         var v = this.el.getValue();
37293         
37294         return v;
37295     },
37296
37297     /**
37298      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37299      * @return {Mixed} value The field value
37300      */
37301     getValue : function(){
37302         var v = this.el.getValue();
37303          
37304         return v;
37305     },
37306
37307     /**
37308      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37309      * @param {Mixed} value The value to set
37310      */
37311     setRawValue : function(v){
37312         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37313     },
37314
37315     /**
37316      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37317      * @param {Mixed} value The value to set
37318      */
37319     setValue : function(v){
37320         this.value = v;
37321         if(this.rendered){
37322             this.el.dom.value = (v === null || v === undefined ? '' : v);
37323              this.validate();
37324         }
37325     },
37326
37327     adjustSize : function(w, h){
37328         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37329         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37330         return s;
37331     },
37332
37333     adjustWidth : function(tag, w){
37334         tag = tag.toLowerCase();
37335         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37336             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37337                 if(tag == 'input'){
37338                     return w + 2;
37339                 }
37340                 if(tag == 'textarea'){
37341                     return w-2;
37342                 }
37343             }else if(Roo.isOpera){
37344                 if(tag == 'input'){
37345                     return w + 2;
37346                 }
37347                 if(tag == 'textarea'){
37348                     return w-2;
37349                 }
37350             }
37351         }
37352         return w;
37353     }
37354 });
37355
37356
37357 // anything other than normal should be considered experimental
37358 Roo.form.Field.msgFx = {
37359     normal : {
37360         show: function(msgEl, f){
37361             msgEl.setDisplayed('block');
37362         },
37363
37364         hide : function(msgEl, f){
37365             msgEl.setDisplayed(false).update('');
37366         }
37367     },
37368
37369     slide : {
37370         show: function(msgEl, f){
37371             msgEl.slideIn('t', {stopFx:true});
37372         },
37373
37374         hide : function(msgEl, f){
37375             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37376         }
37377     },
37378
37379     slideRight : {
37380         show: function(msgEl, f){
37381             msgEl.fixDisplay();
37382             msgEl.alignTo(f.el, 'tl-tr');
37383             msgEl.slideIn('l', {stopFx:true});
37384         },
37385
37386         hide : function(msgEl, f){
37387             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37388         }
37389     }
37390 };/*
37391  * Based on:
37392  * Ext JS Library 1.1.1
37393  * Copyright(c) 2006-2007, Ext JS, LLC.
37394  *
37395  * Originally Released Under LGPL - original licence link has changed is not relivant.
37396  *
37397  * Fork - LGPL
37398  * <script type="text/javascript">
37399  */
37400  
37401
37402 /**
37403  * @class Roo.form.TextField
37404  * @extends Roo.form.Field
37405  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37406  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37407  * @constructor
37408  * Creates a new TextField
37409  * @param {Object} config Configuration options
37410  */
37411 Roo.form.TextField = function(config){
37412     Roo.form.TextField.superclass.constructor.call(this, config);
37413     this.addEvents({
37414         /**
37415          * @event autosize
37416          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37417          * according to the default logic, but this event provides a hook for the developer to apply additional
37418          * logic at runtime to resize the field if needed.
37419              * @param {Roo.form.Field} this This text field
37420              * @param {Number} width The new field width
37421              */
37422         autosize : true
37423     });
37424 };
37425
37426 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37427     /**
37428      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37429      */
37430     grow : false,
37431     /**
37432      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37433      */
37434     growMin : 30,
37435     /**
37436      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37437      */
37438     growMax : 800,
37439     /**
37440      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37441      */
37442     vtype : null,
37443     /**
37444      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37445      */
37446     maskRe : null,
37447     /**
37448      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37449      */
37450     disableKeyFilter : false,
37451     /**
37452      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37453      */
37454     allowBlank : true,
37455     /**
37456      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37457      */
37458     minLength : 0,
37459     /**
37460      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37461      */
37462     maxLength : Number.MAX_VALUE,
37463     /**
37464      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37465      */
37466     minLengthText : "The minimum length for this field is {0}",
37467     /**
37468      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37469      */
37470     maxLengthText : "The maximum length for this field is {0}",
37471     /**
37472      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37473      */
37474     selectOnFocus : false,
37475     /**
37476      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37477      */
37478     blankText : "This field is required",
37479     /**
37480      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37481      * If available, this function will be called only after the basic validators all return true, and will be passed the
37482      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37483      */
37484     validator : null,
37485     /**
37486      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37487      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37488      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37489      */
37490     regex : null,
37491     /**
37492      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37493      */
37494     regexText : "",
37495     /**
37496      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37497      */
37498     emptyText : null,
37499    
37500
37501     // private
37502     initEvents : function()
37503     {
37504         if (this.emptyText) {
37505             this.el.attr('placeholder', this.emptyText);
37506         }
37507         
37508         Roo.form.TextField.superclass.initEvents.call(this);
37509         if(this.validationEvent == 'keyup'){
37510             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37511             this.el.on('keyup', this.filterValidation, this);
37512         }
37513         else if(this.validationEvent !== false){
37514             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37515         }
37516         
37517         if(this.selectOnFocus){
37518             this.on("focus", this.preFocus, this);
37519             
37520         }
37521         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37522             this.el.on("keypress", this.filterKeys, this);
37523         }
37524         if(this.grow){
37525             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37526             this.el.on("click", this.autoSize,  this);
37527         }
37528         if(this.el.is('input[type=password]') && Roo.isSafari){
37529             this.el.on('keydown', this.SafariOnKeyDown, this);
37530         }
37531     },
37532
37533     processValue : function(value){
37534         if(this.stripCharsRe){
37535             var newValue = value.replace(this.stripCharsRe, '');
37536             if(newValue !== value){
37537                 this.setRawValue(newValue);
37538                 return newValue;
37539             }
37540         }
37541         return value;
37542     },
37543
37544     filterValidation : function(e){
37545         if(!e.isNavKeyPress()){
37546             this.validationTask.delay(this.validationDelay);
37547         }
37548     },
37549
37550     // private
37551     onKeyUp : function(e){
37552         if(!e.isNavKeyPress()){
37553             this.autoSize();
37554         }
37555     },
37556
37557     /**
37558      * Resets the current field value to the originally-loaded value and clears any validation messages.
37559      *  
37560      */
37561     reset : function(){
37562         Roo.form.TextField.superclass.reset.call(this);
37563        
37564     },
37565
37566     
37567     // private
37568     preFocus : function(){
37569         
37570         if(this.selectOnFocus){
37571             this.el.dom.select();
37572         }
37573     },
37574
37575     
37576     // private
37577     filterKeys : function(e){
37578         var k = e.getKey();
37579         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37580             return;
37581         }
37582         var c = e.getCharCode(), cc = String.fromCharCode(c);
37583         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37584             return;
37585         }
37586         if(!this.maskRe.test(cc)){
37587             e.stopEvent();
37588         }
37589     },
37590
37591     setValue : function(v){
37592         
37593         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37594         
37595         this.autoSize();
37596     },
37597
37598     /**
37599      * Validates a value according to the field's validation rules and marks the field as invalid
37600      * if the validation fails
37601      * @param {Mixed} value The value to validate
37602      * @return {Boolean} True if the value is valid, else false
37603      */
37604     validateValue : function(value){
37605         if(value.length < 1)  { // if it's blank
37606              if(this.allowBlank){
37607                 this.clearInvalid();
37608                 return true;
37609              }else{
37610                 this.markInvalid(this.blankText);
37611                 return false;
37612              }
37613         }
37614         if(value.length < this.minLength){
37615             this.markInvalid(String.format(this.minLengthText, this.minLength));
37616             return false;
37617         }
37618         if(value.length > this.maxLength){
37619             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37620             return false;
37621         }
37622         if(this.vtype){
37623             var vt = Roo.form.VTypes;
37624             if(!vt[this.vtype](value, this)){
37625                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37626                 return false;
37627             }
37628         }
37629         if(typeof this.validator == "function"){
37630             var msg = this.validator(value);
37631             if(msg !== true){
37632                 this.markInvalid(msg);
37633                 return false;
37634             }
37635         }
37636         if(this.regex && !this.regex.test(value)){
37637             this.markInvalid(this.regexText);
37638             return false;
37639         }
37640         return true;
37641     },
37642
37643     /**
37644      * Selects text in this field
37645      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37646      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37647      */
37648     selectText : function(start, end){
37649         var v = this.getRawValue();
37650         if(v.length > 0){
37651             start = start === undefined ? 0 : start;
37652             end = end === undefined ? v.length : end;
37653             var d = this.el.dom;
37654             if(d.setSelectionRange){
37655                 d.setSelectionRange(start, end);
37656             }else if(d.createTextRange){
37657                 var range = d.createTextRange();
37658                 range.moveStart("character", start);
37659                 range.moveEnd("character", v.length-end);
37660                 range.select();
37661             }
37662         }
37663     },
37664
37665     /**
37666      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37667      * This only takes effect if grow = true, and fires the autosize event.
37668      */
37669     autoSize : function(){
37670         if(!this.grow || !this.rendered){
37671             return;
37672         }
37673         if(!this.metrics){
37674             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37675         }
37676         var el = this.el;
37677         var v = el.dom.value;
37678         var d = document.createElement('div');
37679         d.appendChild(document.createTextNode(v));
37680         v = d.innerHTML;
37681         d = null;
37682         v += "&#160;";
37683         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37684         this.el.setWidth(w);
37685         this.fireEvent("autosize", this, w);
37686     },
37687     
37688     // private
37689     SafariOnKeyDown : function(event)
37690     {
37691         // this is a workaround for a password hang bug on chrome/ webkit.
37692         
37693         var isSelectAll = false;
37694         
37695         if(this.el.dom.selectionEnd > 0){
37696             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37697         }
37698         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37699             event.preventDefault();
37700             this.setValue('');
37701             return;
37702         }
37703         
37704         if(isSelectAll){ // backspace and delete key
37705             
37706             event.preventDefault();
37707             // this is very hacky as keydown always get's upper case.
37708             //
37709             var cc = String.fromCharCode(event.getCharCode());
37710             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37711             
37712         }
37713         
37714         
37715     }
37716 });/*
37717  * Based on:
37718  * Ext JS Library 1.1.1
37719  * Copyright(c) 2006-2007, Ext JS, LLC.
37720  *
37721  * Originally Released Under LGPL - original licence link has changed is not relivant.
37722  *
37723  * Fork - LGPL
37724  * <script type="text/javascript">
37725  */
37726  
37727 /**
37728  * @class Roo.form.Hidden
37729  * @extends Roo.form.TextField
37730  * Simple Hidden element used on forms 
37731  * 
37732  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37733  * 
37734  * @constructor
37735  * Creates a new Hidden form element.
37736  * @param {Object} config Configuration options
37737  */
37738
37739
37740
37741 // easy hidden field...
37742 Roo.form.Hidden = function(config){
37743     Roo.form.Hidden.superclass.constructor.call(this, config);
37744 };
37745   
37746 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37747     fieldLabel:      '',
37748     inputType:      'hidden',
37749     width:          50,
37750     allowBlank:     true,
37751     labelSeparator: '',
37752     hidden:         true,
37753     itemCls :       'x-form-item-display-none'
37754
37755
37756 });
37757
37758
37759 /*
37760  * Based on:
37761  * Ext JS Library 1.1.1
37762  * Copyright(c) 2006-2007, Ext JS, LLC.
37763  *
37764  * Originally Released Under LGPL - original licence link has changed is not relivant.
37765  *
37766  * Fork - LGPL
37767  * <script type="text/javascript">
37768  */
37769  
37770 /**
37771  * @class Roo.form.TriggerField
37772  * @extends Roo.form.TextField
37773  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37774  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37775  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37776  * for which you can provide a custom implementation.  For example:
37777  * <pre><code>
37778 var trigger = new Roo.form.TriggerField();
37779 trigger.onTriggerClick = myTriggerFn;
37780 trigger.applyTo('my-field');
37781 </code></pre>
37782  *
37783  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37784  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37785  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37786  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37787  * @constructor
37788  * Create a new TriggerField.
37789  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37790  * to the base TextField)
37791  */
37792 Roo.form.TriggerField = function(config){
37793     this.mimicing = false;
37794     Roo.form.TriggerField.superclass.constructor.call(this, config);
37795 };
37796
37797 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37798     /**
37799      * @cfg {String} triggerClass A CSS class to apply to the trigger
37800      */
37801     /**
37802      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37803      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37804      */
37805     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37806     /**
37807      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37808      */
37809     hideTrigger:false,
37810
37811     /** @cfg {Boolean} grow @hide */
37812     /** @cfg {Number} growMin @hide */
37813     /** @cfg {Number} growMax @hide */
37814
37815     /**
37816      * @hide 
37817      * @method
37818      */
37819     autoSize: Roo.emptyFn,
37820     // private
37821     monitorTab : true,
37822     // private
37823     deferHeight : true,
37824
37825     
37826     actionMode : 'wrap',
37827     // private
37828     onResize : function(w, h){
37829         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37830         if(typeof w == 'number'){
37831             var x = w - this.trigger.getWidth();
37832             this.el.setWidth(this.adjustWidth('input', x));
37833             this.trigger.setStyle('left', x+'px');
37834         }
37835     },
37836
37837     // private
37838     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37839
37840     // private
37841     getResizeEl : function(){
37842         return this.wrap;
37843     },
37844
37845     // private
37846     getPositionEl : function(){
37847         return this.wrap;
37848     },
37849
37850     // private
37851     alignErrorIcon : function(){
37852         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37853     },
37854
37855     // private
37856     onRender : function(ct, position){
37857         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37858         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37859         this.trigger = this.wrap.createChild(this.triggerConfig ||
37860                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37861         if(this.hideTrigger){
37862             this.trigger.setDisplayed(false);
37863         }
37864         this.initTrigger();
37865         if(!this.width){
37866             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37867         }
37868     },
37869
37870     // private
37871     initTrigger : function(){
37872         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37873         this.trigger.addClassOnOver('x-form-trigger-over');
37874         this.trigger.addClassOnClick('x-form-trigger-click');
37875     },
37876
37877     // private
37878     onDestroy : function(){
37879         if(this.trigger){
37880             this.trigger.removeAllListeners();
37881             this.trigger.remove();
37882         }
37883         if(this.wrap){
37884             this.wrap.remove();
37885         }
37886         Roo.form.TriggerField.superclass.onDestroy.call(this);
37887     },
37888
37889     // private
37890     onFocus : function(){
37891         Roo.form.TriggerField.superclass.onFocus.call(this);
37892         if(!this.mimicing){
37893             this.wrap.addClass('x-trigger-wrap-focus');
37894             this.mimicing = true;
37895             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37896             if(this.monitorTab){
37897                 this.el.on("keydown", this.checkTab, this);
37898             }
37899         }
37900     },
37901
37902     // private
37903     checkTab : function(e){
37904         if(e.getKey() == e.TAB){
37905             this.triggerBlur();
37906         }
37907     },
37908
37909     // private
37910     onBlur : function(){
37911         // do nothing
37912     },
37913
37914     // private
37915     mimicBlur : function(e, t){
37916         if(!this.wrap.contains(t) && this.validateBlur()){
37917             this.triggerBlur();
37918         }
37919     },
37920
37921     // private
37922     triggerBlur : function(){
37923         this.mimicing = false;
37924         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37925         if(this.monitorTab){
37926             this.el.un("keydown", this.checkTab, this);
37927         }
37928         this.wrap.removeClass('x-trigger-wrap-focus');
37929         Roo.form.TriggerField.superclass.onBlur.call(this);
37930     },
37931
37932     // private
37933     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37934     validateBlur : function(e, t){
37935         return true;
37936     },
37937
37938     // private
37939     onDisable : function(){
37940         Roo.form.TriggerField.superclass.onDisable.call(this);
37941         if(this.wrap){
37942             this.wrap.addClass('x-item-disabled');
37943         }
37944     },
37945
37946     // private
37947     onEnable : function(){
37948         Roo.form.TriggerField.superclass.onEnable.call(this);
37949         if(this.wrap){
37950             this.wrap.removeClass('x-item-disabled');
37951         }
37952     },
37953
37954     // private
37955     onShow : function(){
37956         var ae = this.getActionEl();
37957         
37958         if(ae){
37959             ae.dom.style.display = '';
37960             ae.dom.style.visibility = 'visible';
37961         }
37962     },
37963
37964     // private
37965     
37966     onHide : function(){
37967         var ae = this.getActionEl();
37968         ae.dom.style.display = 'none';
37969     },
37970
37971     /**
37972      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37973      * by an implementing function.
37974      * @method
37975      * @param {EventObject} e
37976      */
37977     onTriggerClick : Roo.emptyFn
37978 });
37979
37980 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37981 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37982 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37983 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37984     initComponent : function(){
37985         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37986
37987         this.triggerConfig = {
37988             tag:'span', cls:'x-form-twin-triggers', cn:[
37989             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37990             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37991         ]};
37992     },
37993
37994     getTrigger : function(index){
37995         return this.triggers[index];
37996     },
37997
37998     initTrigger : function(){
37999         var ts = this.trigger.select('.x-form-trigger', true);
38000         this.wrap.setStyle('overflow', 'hidden');
38001         var triggerField = this;
38002         ts.each(function(t, all, index){
38003             t.hide = function(){
38004                 var w = triggerField.wrap.getWidth();
38005                 this.dom.style.display = 'none';
38006                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38007             };
38008             t.show = function(){
38009                 var w = triggerField.wrap.getWidth();
38010                 this.dom.style.display = '';
38011                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38012             };
38013             var triggerIndex = 'Trigger'+(index+1);
38014
38015             if(this['hide'+triggerIndex]){
38016                 t.dom.style.display = 'none';
38017             }
38018             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38019             t.addClassOnOver('x-form-trigger-over');
38020             t.addClassOnClick('x-form-trigger-click');
38021         }, this);
38022         this.triggers = ts.elements;
38023     },
38024
38025     onTrigger1Click : Roo.emptyFn,
38026     onTrigger2Click : Roo.emptyFn
38027 });/*
38028  * Based on:
38029  * Ext JS Library 1.1.1
38030  * Copyright(c) 2006-2007, Ext JS, LLC.
38031  *
38032  * Originally Released Under LGPL - original licence link has changed is not relivant.
38033  *
38034  * Fork - LGPL
38035  * <script type="text/javascript">
38036  */
38037  
38038 /**
38039  * @class Roo.form.TextArea
38040  * @extends Roo.form.TextField
38041  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38042  * support for auto-sizing.
38043  * @constructor
38044  * Creates a new TextArea
38045  * @param {Object} config Configuration options
38046  */
38047 Roo.form.TextArea = function(config){
38048     Roo.form.TextArea.superclass.constructor.call(this, config);
38049     // these are provided exchanges for backwards compat
38050     // minHeight/maxHeight were replaced by growMin/growMax to be
38051     // compatible with TextField growing config values
38052     if(this.minHeight !== undefined){
38053         this.growMin = this.minHeight;
38054     }
38055     if(this.maxHeight !== undefined){
38056         this.growMax = this.maxHeight;
38057     }
38058 };
38059
38060 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38061     /**
38062      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38063      */
38064     growMin : 60,
38065     /**
38066      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38067      */
38068     growMax: 1000,
38069     /**
38070      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38071      * in the field (equivalent to setting overflow: hidden, defaults to false)
38072      */
38073     preventScrollbars: false,
38074     /**
38075      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38076      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38077      */
38078
38079     // private
38080     onRender : function(ct, position){
38081         if(!this.el){
38082             this.defaultAutoCreate = {
38083                 tag: "textarea",
38084                 style:"width:300px;height:60px;",
38085                 autocomplete: "off"
38086             };
38087         }
38088         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38089         if(this.grow){
38090             this.textSizeEl = Roo.DomHelper.append(document.body, {
38091                 tag: "pre", cls: "x-form-grow-sizer"
38092             });
38093             if(this.preventScrollbars){
38094                 this.el.setStyle("overflow", "hidden");
38095             }
38096             this.el.setHeight(this.growMin);
38097         }
38098     },
38099
38100     onDestroy : function(){
38101         if(this.textSizeEl){
38102             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38103         }
38104         Roo.form.TextArea.superclass.onDestroy.call(this);
38105     },
38106
38107     // private
38108     onKeyUp : function(e){
38109         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38110             this.autoSize();
38111         }
38112     },
38113
38114     /**
38115      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38116      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38117      */
38118     autoSize : function(){
38119         if(!this.grow || !this.textSizeEl){
38120             return;
38121         }
38122         var el = this.el;
38123         var v = el.dom.value;
38124         var ts = this.textSizeEl;
38125
38126         ts.innerHTML = '';
38127         ts.appendChild(document.createTextNode(v));
38128         v = ts.innerHTML;
38129
38130         Roo.fly(ts).setWidth(this.el.getWidth());
38131         if(v.length < 1){
38132             v = "&#160;&#160;";
38133         }else{
38134             if(Roo.isIE){
38135                 v = v.replace(/\n/g, '<p>&#160;</p>');
38136             }
38137             v += "&#160;\n&#160;";
38138         }
38139         ts.innerHTML = v;
38140         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38141         if(h != this.lastHeight){
38142             this.lastHeight = h;
38143             this.el.setHeight(h);
38144             this.fireEvent("autosize", this, h);
38145         }
38146     }
38147 });/*
38148  * Based on:
38149  * Ext JS Library 1.1.1
38150  * Copyright(c) 2006-2007, Ext JS, LLC.
38151  *
38152  * Originally Released Under LGPL - original licence link has changed is not relivant.
38153  *
38154  * Fork - LGPL
38155  * <script type="text/javascript">
38156  */
38157  
38158
38159 /**
38160  * @class Roo.form.NumberField
38161  * @extends Roo.form.TextField
38162  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38163  * @constructor
38164  * Creates a new NumberField
38165  * @param {Object} config Configuration options
38166  */
38167 Roo.form.NumberField = function(config){
38168     Roo.form.NumberField.superclass.constructor.call(this, config);
38169 };
38170
38171 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38172     /**
38173      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38174      */
38175     fieldClass: "x-form-field x-form-num-field",
38176     /**
38177      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38178      */
38179     allowDecimals : true,
38180     /**
38181      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38182      */
38183     decimalSeparator : ".",
38184     /**
38185      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38186      */
38187     decimalPrecision : 2,
38188     /**
38189      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38190      */
38191     allowNegative : true,
38192     /**
38193      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38194      */
38195     minValue : Number.NEGATIVE_INFINITY,
38196     /**
38197      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38198      */
38199     maxValue : Number.MAX_VALUE,
38200     /**
38201      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38202      */
38203     minText : "The minimum value for this field is {0}",
38204     /**
38205      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38206      */
38207     maxText : "The maximum value for this field is {0}",
38208     /**
38209      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38210      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38211      */
38212     nanText : "{0} is not a valid number",
38213
38214     // private
38215     initEvents : function(){
38216         Roo.form.NumberField.superclass.initEvents.call(this);
38217         var allowed = "0123456789";
38218         if(this.allowDecimals){
38219             allowed += this.decimalSeparator;
38220         }
38221         if(this.allowNegative){
38222             allowed += "-";
38223         }
38224         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38225         var keyPress = function(e){
38226             var k = e.getKey();
38227             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38228                 return;
38229             }
38230             var c = e.getCharCode();
38231             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38232                 e.stopEvent();
38233             }
38234         };
38235         this.el.on("keypress", keyPress, this);
38236     },
38237
38238     // private
38239     validateValue : function(value){
38240         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38241             return false;
38242         }
38243         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38244              return true;
38245         }
38246         var num = this.parseValue(value);
38247         if(isNaN(num)){
38248             this.markInvalid(String.format(this.nanText, value));
38249             return false;
38250         }
38251         if(num < this.minValue){
38252             this.markInvalid(String.format(this.minText, this.minValue));
38253             return false;
38254         }
38255         if(num > this.maxValue){
38256             this.markInvalid(String.format(this.maxText, this.maxValue));
38257             return false;
38258         }
38259         return true;
38260     },
38261
38262     getValue : function(){
38263         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38264     },
38265
38266     // private
38267     parseValue : function(value){
38268         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38269         return isNaN(value) ? '' : value;
38270     },
38271
38272     // private
38273     fixPrecision : function(value){
38274         var nan = isNaN(value);
38275         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38276             return nan ? '' : value;
38277         }
38278         return parseFloat(value).toFixed(this.decimalPrecision);
38279     },
38280
38281     setValue : function(v){
38282         v = this.fixPrecision(v);
38283         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38284     },
38285
38286     // private
38287     decimalPrecisionFcn : function(v){
38288         return Math.floor(v);
38289     },
38290
38291     beforeBlur : function(){
38292         var v = this.parseValue(this.getRawValue());
38293         if(v){
38294             this.setValue(v);
38295         }
38296     }
38297 });/*
38298  * Based on:
38299  * Ext JS Library 1.1.1
38300  * Copyright(c) 2006-2007, Ext JS, LLC.
38301  *
38302  * Originally Released Under LGPL - original licence link has changed is not relivant.
38303  *
38304  * Fork - LGPL
38305  * <script type="text/javascript">
38306  */
38307  
38308 /**
38309  * @class Roo.form.DateField
38310  * @extends Roo.form.TriggerField
38311  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38312 * @constructor
38313 * Create a new DateField
38314 * @param {Object} config
38315  */
38316 Roo.form.DateField = function(config){
38317     Roo.form.DateField.superclass.constructor.call(this, config);
38318     
38319       this.addEvents({
38320          
38321         /**
38322          * @event select
38323          * Fires when a date is selected
38324              * @param {Roo.form.DateField} combo This combo box
38325              * @param {Date} date The date selected
38326              */
38327         'select' : true
38328          
38329     });
38330     
38331     
38332     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38333     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38334     this.ddMatch = null;
38335     if(this.disabledDates){
38336         var dd = this.disabledDates;
38337         var re = "(?:";
38338         for(var i = 0; i < dd.length; i++){
38339             re += dd[i];
38340             if(i != dd.length-1) re += "|";
38341         }
38342         this.ddMatch = new RegExp(re + ")");
38343     }
38344 };
38345
38346 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38347     /**
38348      * @cfg {String} format
38349      * The default date format string which can be overriden for localization support.  The format must be
38350      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38351      */
38352     format : "m/d/y",
38353     /**
38354      * @cfg {String} altFormats
38355      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38356      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38357      */
38358     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38359     /**
38360      * @cfg {Array} disabledDays
38361      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38362      */
38363     disabledDays : null,
38364     /**
38365      * @cfg {String} disabledDaysText
38366      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38367      */
38368     disabledDaysText : "Disabled",
38369     /**
38370      * @cfg {Array} disabledDates
38371      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38372      * expression so they are very powerful. Some examples:
38373      * <ul>
38374      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38375      * <li>["03/08", "09/16"] would disable those days for every year</li>
38376      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38377      * <li>["03/../2006"] would disable every day in March 2006</li>
38378      * <li>["^03"] would disable every day in every March</li>
38379      * </ul>
38380      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38381      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38382      */
38383     disabledDates : null,
38384     /**
38385      * @cfg {String} disabledDatesText
38386      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38387      */
38388     disabledDatesText : "Disabled",
38389     /**
38390      * @cfg {Date/String} minValue
38391      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38392      * valid format (defaults to null).
38393      */
38394     minValue : null,
38395     /**
38396      * @cfg {Date/String} maxValue
38397      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38398      * valid format (defaults to null).
38399      */
38400     maxValue : null,
38401     /**
38402      * @cfg {String} minText
38403      * The error text to display when the date in the cell is before minValue (defaults to
38404      * 'The date in this field must be after {minValue}').
38405      */
38406     minText : "The date in this field must be equal to or after {0}",
38407     /**
38408      * @cfg {String} maxText
38409      * The error text to display when the date in the cell is after maxValue (defaults to
38410      * 'The date in this field must be before {maxValue}').
38411      */
38412     maxText : "The date in this field must be equal to or before {0}",
38413     /**
38414      * @cfg {String} invalidText
38415      * The error text to display when the date in the field is invalid (defaults to
38416      * '{value} is not a valid date - it must be in the format {format}').
38417      */
38418     invalidText : "{0} is not a valid date - it must be in the format {1}",
38419     /**
38420      * @cfg {String} triggerClass
38421      * An additional CSS class used to style the trigger button.  The trigger will always get the
38422      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38423      * which displays a calendar icon).
38424      */
38425     triggerClass : 'x-form-date-trigger',
38426     
38427
38428     /**
38429      * @cfg {Boolean} useIso
38430      * if enabled, then the date field will use a hidden field to store the 
38431      * real value as iso formated date. default (false)
38432      */ 
38433     useIso : false,
38434     /**
38435      * @cfg {String/Object} autoCreate
38436      * A DomHelper element spec, or true for a default element spec (defaults to
38437      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38438      */ 
38439     // private
38440     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38441     
38442     // private
38443     hiddenField: false,
38444     
38445     onRender : function(ct, position)
38446     {
38447         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38448         if (this.useIso) {
38449             //this.el.dom.removeAttribute('name'); 
38450             Roo.log("Changing name?");
38451             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38452             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38453                     'before', true);
38454             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38455             // prevent input submission
38456             this.hiddenName = this.name;
38457         }
38458             
38459             
38460     },
38461     
38462     // private
38463     validateValue : function(value)
38464     {
38465         value = this.formatDate(value);
38466         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38467             Roo.log('super failed');
38468             return false;
38469         }
38470         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38471              return true;
38472         }
38473         var svalue = value;
38474         value = this.parseDate(value);
38475         if(!value){
38476             Roo.log('parse date failed' + svalue);
38477             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38478             return false;
38479         }
38480         var time = value.getTime();
38481         if(this.minValue && time < this.minValue.getTime()){
38482             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38483             return false;
38484         }
38485         if(this.maxValue && time > this.maxValue.getTime()){
38486             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38487             return false;
38488         }
38489         if(this.disabledDays){
38490             var day = value.getDay();
38491             for(var i = 0; i < this.disabledDays.length; i++) {
38492                 if(day === this.disabledDays[i]){
38493                     this.markInvalid(this.disabledDaysText);
38494                     return false;
38495                 }
38496             }
38497         }
38498         var fvalue = this.formatDate(value);
38499         if(this.ddMatch && this.ddMatch.test(fvalue)){
38500             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38501             return false;
38502         }
38503         return true;
38504     },
38505
38506     // private
38507     // Provides logic to override the default TriggerField.validateBlur which just returns true
38508     validateBlur : function(){
38509         return !this.menu || !this.menu.isVisible();
38510     },
38511     
38512     getName: function()
38513     {
38514         // returns hidden if it's set..
38515         if (!this.rendered) {return ''};
38516         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38517         
38518     },
38519
38520     /**
38521      * Returns the current date value of the date field.
38522      * @return {Date} The date value
38523      */
38524     getValue : function(){
38525         
38526         return  this.hiddenField ?
38527                 this.hiddenField.value :
38528                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38529     },
38530
38531     /**
38532      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38533      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38534      * (the default format used is "m/d/y").
38535      * <br />Usage:
38536      * <pre><code>
38537 //All of these calls set the same date value (May 4, 2006)
38538
38539 //Pass a date object:
38540 var dt = new Date('5/4/06');
38541 dateField.setValue(dt);
38542
38543 //Pass a date string (default format):
38544 dateField.setValue('5/4/06');
38545
38546 //Pass a date string (custom format):
38547 dateField.format = 'Y-m-d';
38548 dateField.setValue('2006-5-4');
38549 </code></pre>
38550      * @param {String/Date} date The date or valid date string
38551      */
38552     setValue : function(date){
38553         if (this.hiddenField) {
38554             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38555         }
38556         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38557         // make sure the value field is always stored as a date..
38558         this.value = this.parseDate(date);
38559         
38560         
38561     },
38562
38563     // private
38564     parseDate : function(value){
38565         if(!value || value instanceof Date){
38566             return value;
38567         }
38568         var v = Date.parseDate(value, this.format);
38569          if (!v && this.useIso) {
38570             v = Date.parseDate(value, 'Y-m-d');
38571         }
38572         if(!v && this.altFormats){
38573             if(!this.altFormatsArray){
38574                 this.altFormatsArray = this.altFormats.split("|");
38575             }
38576             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38577                 v = Date.parseDate(value, this.altFormatsArray[i]);
38578             }
38579         }
38580         return v;
38581     },
38582
38583     // private
38584     formatDate : function(date, fmt){
38585         return (!date || !(date instanceof Date)) ?
38586                date : date.dateFormat(fmt || this.format);
38587     },
38588
38589     // private
38590     menuListeners : {
38591         select: function(m, d){
38592             
38593             this.setValue(d);
38594             this.fireEvent('select', this, d);
38595         },
38596         show : function(){ // retain focus styling
38597             this.onFocus();
38598         },
38599         hide : function(){
38600             this.focus.defer(10, this);
38601             var ml = this.menuListeners;
38602             this.menu.un("select", ml.select,  this);
38603             this.menu.un("show", ml.show,  this);
38604             this.menu.un("hide", ml.hide,  this);
38605         }
38606     },
38607
38608     // private
38609     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38610     onTriggerClick : function(){
38611         if(this.disabled){
38612             return;
38613         }
38614         if(this.menu == null){
38615             this.menu = new Roo.menu.DateMenu();
38616         }
38617         Roo.apply(this.menu.picker,  {
38618             showClear: this.allowBlank,
38619             minDate : this.minValue,
38620             maxDate : this.maxValue,
38621             disabledDatesRE : this.ddMatch,
38622             disabledDatesText : this.disabledDatesText,
38623             disabledDays : this.disabledDays,
38624             disabledDaysText : this.disabledDaysText,
38625             format : this.useIso ? 'Y-m-d' : this.format,
38626             minText : String.format(this.minText, this.formatDate(this.minValue)),
38627             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38628         });
38629         this.menu.on(Roo.apply({}, this.menuListeners, {
38630             scope:this
38631         }));
38632         this.menu.picker.setValue(this.getValue() || new Date());
38633         this.menu.show(this.el, "tl-bl?");
38634     },
38635
38636     beforeBlur : function(){
38637         var v = this.parseDate(this.getRawValue());
38638         if(v){
38639             this.setValue(v);
38640         }
38641     },
38642
38643     /*@
38644      * overide
38645      * 
38646      */
38647     isDirty : function() {
38648         if(this.disabled) {
38649             return false;
38650         }
38651         
38652         if(typeof(this.startValue) === 'undefined'){
38653             return false;
38654         }
38655         
38656         return String(this.getValue()) !== String(this.startValue);
38657         
38658     }
38659 });/*
38660  * Based on:
38661  * Ext JS Library 1.1.1
38662  * Copyright(c) 2006-2007, Ext JS, LLC.
38663  *
38664  * Originally Released Under LGPL - original licence link has changed is not relivant.
38665  *
38666  * Fork - LGPL
38667  * <script type="text/javascript">
38668  */
38669  
38670 /**
38671  * @class Roo.form.MonthField
38672  * @extends Roo.form.TriggerField
38673  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38674 * @constructor
38675 * Create a new MonthField
38676 * @param {Object} config
38677  */
38678 Roo.form.MonthField = function(config){
38679     
38680     Roo.form.MonthField.superclass.constructor.call(this, config);
38681     
38682       this.addEvents({
38683          
38684         /**
38685          * @event select
38686          * Fires when a date is selected
38687              * @param {Roo.form.MonthFieeld} combo This combo box
38688              * @param {Date} date The date selected
38689              */
38690         'select' : true
38691          
38692     });
38693     
38694     
38695     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38696     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38697     this.ddMatch = null;
38698     if(this.disabledDates){
38699         var dd = this.disabledDates;
38700         var re = "(?:";
38701         for(var i = 0; i < dd.length; i++){
38702             re += dd[i];
38703             if(i != dd.length-1) re += "|";
38704         }
38705         this.ddMatch = new RegExp(re + ")");
38706     }
38707 };
38708
38709 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38710     /**
38711      * @cfg {String} format
38712      * The default date format string which can be overriden for localization support.  The format must be
38713      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38714      */
38715     format : "M Y",
38716     /**
38717      * @cfg {String} altFormats
38718      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38719      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38720      */
38721     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38722     /**
38723      * @cfg {Array} disabledDays
38724      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38725      */
38726     disabledDays : [0,1,2,3,4,5,6],
38727     /**
38728      * @cfg {String} disabledDaysText
38729      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38730      */
38731     disabledDaysText : "Disabled",
38732     /**
38733      * @cfg {Array} disabledDates
38734      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38735      * expression so they are very powerful. Some examples:
38736      * <ul>
38737      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38738      * <li>["03/08", "09/16"] would disable those days for every year</li>
38739      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38740      * <li>["03/../2006"] would disable every day in March 2006</li>
38741      * <li>["^03"] would disable every day in every March</li>
38742      * </ul>
38743      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38744      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38745      */
38746     disabledDates : null,
38747     /**
38748      * @cfg {String} disabledDatesText
38749      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38750      */
38751     disabledDatesText : "Disabled",
38752     /**
38753      * @cfg {Date/String} minValue
38754      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38755      * valid format (defaults to null).
38756      */
38757     minValue : null,
38758     /**
38759      * @cfg {Date/String} maxValue
38760      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38761      * valid format (defaults to null).
38762      */
38763     maxValue : null,
38764     /**
38765      * @cfg {String} minText
38766      * The error text to display when the date in the cell is before minValue (defaults to
38767      * 'The date in this field must be after {minValue}').
38768      */
38769     minText : "The date in this field must be equal to or after {0}",
38770     /**
38771      * @cfg {String} maxTextf
38772      * The error text to display when the date in the cell is after maxValue (defaults to
38773      * 'The date in this field must be before {maxValue}').
38774      */
38775     maxText : "The date in this field must be equal to or before {0}",
38776     /**
38777      * @cfg {String} invalidText
38778      * The error text to display when the date in the field is invalid (defaults to
38779      * '{value} is not a valid date - it must be in the format {format}').
38780      */
38781     invalidText : "{0} is not a valid date - it must be in the format {1}",
38782     /**
38783      * @cfg {String} triggerClass
38784      * An additional CSS class used to style the trigger button.  The trigger will always get the
38785      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38786      * which displays a calendar icon).
38787      */
38788     triggerClass : 'x-form-date-trigger',
38789     
38790
38791     /**
38792      * @cfg {Boolean} useIso
38793      * if enabled, then the date field will use a hidden field to store the 
38794      * real value as iso formated date. default (true)
38795      */ 
38796     useIso : true,
38797     /**
38798      * @cfg {String/Object} autoCreate
38799      * A DomHelper element spec, or true for a default element spec (defaults to
38800      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38801      */ 
38802     // private
38803     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38804     
38805     // private
38806     hiddenField: false,
38807     
38808     hideMonthPicker : false,
38809     
38810     onRender : function(ct, position)
38811     {
38812         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38813         if (this.useIso) {
38814             this.el.dom.removeAttribute('name'); 
38815             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38816                     'before', true);
38817             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38818             // prevent input submission
38819             this.hiddenName = this.name;
38820         }
38821             
38822             
38823     },
38824     
38825     // private
38826     validateValue : function(value)
38827     {
38828         value = this.formatDate(value);
38829         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38830             return false;
38831         }
38832         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38833              return true;
38834         }
38835         var svalue = value;
38836         value = this.parseDate(value);
38837         if(!value){
38838             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38839             return false;
38840         }
38841         var time = value.getTime();
38842         if(this.minValue && time < this.minValue.getTime()){
38843             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38844             return false;
38845         }
38846         if(this.maxValue && time > this.maxValue.getTime()){
38847             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38848             return false;
38849         }
38850         /*if(this.disabledDays){
38851             var day = value.getDay();
38852             for(var i = 0; i < this.disabledDays.length; i++) {
38853                 if(day === this.disabledDays[i]){
38854                     this.markInvalid(this.disabledDaysText);
38855                     return false;
38856                 }
38857             }
38858         }
38859         */
38860         var fvalue = this.formatDate(value);
38861         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38862             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38863             return false;
38864         }
38865         */
38866         return true;
38867     },
38868
38869     // private
38870     // Provides logic to override the default TriggerField.validateBlur which just returns true
38871     validateBlur : function(){
38872         return !this.menu || !this.menu.isVisible();
38873     },
38874
38875     /**
38876      * Returns the current date value of the date field.
38877      * @return {Date} The date value
38878      */
38879     getValue : function(){
38880         
38881         
38882         
38883         return  this.hiddenField ?
38884                 this.hiddenField.value :
38885                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38886     },
38887
38888     /**
38889      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38890      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38891      * (the default format used is "m/d/y").
38892      * <br />Usage:
38893      * <pre><code>
38894 //All of these calls set the same date value (May 4, 2006)
38895
38896 //Pass a date object:
38897 var dt = new Date('5/4/06');
38898 monthField.setValue(dt);
38899
38900 //Pass a date string (default format):
38901 monthField.setValue('5/4/06');
38902
38903 //Pass a date string (custom format):
38904 monthField.format = 'Y-m-d';
38905 monthField.setValue('2006-5-4');
38906 </code></pre>
38907      * @param {String/Date} date The date or valid date string
38908      */
38909     setValue : function(date){
38910         Roo.log('month setValue' + date);
38911         // can only be first of month..
38912         
38913         var val = this.parseDate(date);
38914         
38915         if (this.hiddenField) {
38916             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38917         }
38918         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38919         this.value = this.parseDate(date);
38920     },
38921
38922     // private
38923     parseDate : function(value){
38924         if(!value || value instanceof Date){
38925             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38926             return value;
38927         }
38928         var v = Date.parseDate(value, this.format);
38929         if (!v && this.useIso) {
38930             v = Date.parseDate(value, 'Y-m-d');
38931         }
38932         if (v) {
38933             // 
38934             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38935         }
38936         
38937         
38938         if(!v && this.altFormats){
38939             if(!this.altFormatsArray){
38940                 this.altFormatsArray = this.altFormats.split("|");
38941             }
38942             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38943                 v = Date.parseDate(value, this.altFormatsArray[i]);
38944             }
38945         }
38946         return v;
38947     },
38948
38949     // private
38950     formatDate : function(date, fmt){
38951         return (!date || !(date instanceof Date)) ?
38952                date : date.dateFormat(fmt || this.format);
38953     },
38954
38955     // private
38956     menuListeners : {
38957         select: function(m, d){
38958             this.setValue(d);
38959             this.fireEvent('select', this, d);
38960         },
38961         show : function(){ // retain focus styling
38962             this.onFocus();
38963         },
38964         hide : function(){
38965             this.focus.defer(10, this);
38966             var ml = this.menuListeners;
38967             this.menu.un("select", ml.select,  this);
38968             this.menu.un("show", ml.show,  this);
38969             this.menu.un("hide", ml.hide,  this);
38970         }
38971     },
38972     // private
38973     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38974     onTriggerClick : function(){
38975         if(this.disabled){
38976             return;
38977         }
38978         if(this.menu == null){
38979             this.menu = new Roo.menu.DateMenu();
38980            
38981         }
38982         
38983         Roo.apply(this.menu.picker,  {
38984             
38985             showClear: this.allowBlank,
38986             minDate : this.minValue,
38987             maxDate : this.maxValue,
38988             disabledDatesRE : this.ddMatch,
38989             disabledDatesText : this.disabledDatesText,
38990             
38991             format : this.useIso ? 'Y-m-d' : this.format,
38992             minText : String.format(this.minText, this.formatDate(this.minValue)),
38993             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38994             
38995         });
38996          this.menu.on(Roo.apply({}, this.menuListeners, {
38997             scope:this
38998         }));
38999        
39000         
39001         var m = this.menu;
39002         var p = m.picker;
39003         
39004         // hide month picker get's called when we called by 'before hide';
39005         
39006         var ignorehide = true;
39007         p.hideMonthPicker  = function(disableAnim){
39008             if (ignorehide) {
39009                 return;
39010             }
39011              if(this.monthPicker){
39012                 Roo.log("hideMonthPicker called");
39013                 if(disableAnim === true){
39014                     this.monthPicker.hide();
39015                 }else{
39016                     this.monthPicker.slideOut('t', {duration:.2});
39017                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39018                     p.fireEvent("select", this, this.value);
39019                     m.hide();
39020                 }
39021             }
39022         }
39023         
39024         Roo.log('picker set value');
39025         Roo.log(this.getValue());
39026         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39027         m.show(this.el, 'tl-bl?');
39028         ignorehide  = false;
39029         // this will trigger hideMonthPicker..
39030         
39031         
39032         // hidden the day picker
39033         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39034         
39035         
39036         
39037       
39038         
39039         p.showMonthPicker.defer(100, p);
39040     
39041         
39042        
39043     },
39044
39045     beforeBlur : function(){
39046         var v = this.parseDate(this.getRawValue());
39047         if(v){
39048             this.setValue(v);
39049         }
39050     }
39051
39052     /** @cfg {Boolean} grow @hide */
39053     /** @cfg {Number} growMin @hide */
39054     /** @cfg {Number} growMax @hide */
39055     /**
39056      * @hide
39057      * @method autoSize
39058      */
39059 });/*
39060  * Based on:
39061  * Ext JS Library 1.1.1
39062  * Copyright(c) 2006-2007, Ext JS, LLC.
39063  *
39064  * Originally Released Under LGPL - original licence link has changed is not relivant.
39065  *
39066  * Fork - LGPL
39067  * <script type="text/javascript">
39068  */
39069  
39070
39071 /**
39072  * @class Roo.form.ComboBox
39073  * @extends Roo.form.TriggerField
39074  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39075  * @constructor
39076  * Create a new ComboBox.
39077  * @param {Object} config Configuration options
39078  */
39079 Roo.form.ComboBox = function(config){
39080     Roo.form.ComboBox.superclass.constructor.call(this, config);
39081     this.addEvents({
39082         /**
39083          * @event expand
39084          * Fires when the dropdown list is expanded
39085              * @param {Roo.form.ComboBox} combo This combo box
39086              */
39087         'expand' : true,
39088         /**
39089          * @event collapse
39090          * Fires when the dropdown list is collapsed
39091              * @param {Roo.form.ComboBox} combo This combo box
39092              */
39093         'collapse' : true,
39094         /**
39095          * @event beforeselect
39096          * Fires before a list item is selected. Return false to cancel the selection.
39097              * @param {Roo.form.ComboBox} combo This combo box
39098              * @param {Roo.data.Record} record The data record returned from the underlying store
39099              * @param {Number} index The index of the selected item in the dropdown list
39100              */
39101         'beforeselect' : true,
39102         /**
39103          * @event select
39104          * Fires when a list item is selected
39105              * @param {Roo.form.ComboBox} combo This combo box
39106              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39107              * @param {Number} index The index of the selected item in the dropdown list
39108              */
39109         'select' : true,
39110         /**
39111          * @event beforequery
39112          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39113          * The event object passed has these properties:
39114              * @param {Roo.form.ComboBox} combo This combo box
39115              * @param {String} query The query
39116              * @param {Boolean} forceAll true to force "all" query
39117              * @param {Boolean} cancel true to cancel the query
39118              * @param {Object} e The query event object
39119              */
39120         'beforequery': true,
39121          /**
39122          * @event add
39123          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39124              * @param {Roo.form.ComboBox} combo This combo box
39125              */
39126         'add' : true,
39127         /**
39128          * @event edit
39129          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39130              * @param {Roo.form.ComboBox} combo This combo box
39131              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39132              */
39133         'edit' : true
39134         
39135         
39136     });
39137     if(this.transform){
39138         this.allowDomMove = false;
39139         var s = Roo.getDom(this.transform);
39140         if(!this.hiddenName){
39141             this.hiddenName = s.name;
39142         }
39143         if(!this.store){
39144             this.mode = 'local';
39145             var d = [], opts = s.options;
39146             for(var i = 0, len = opts.length;i < len; i++){
39147                 var o = opts[i];
39148                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39149                 if(o.selected) {
39150                     this.value = value;
39151                 }
39152                 d.push([value, o.text]);
39153             }
39154             this.store = new Roo.data.SimpleStore({
39155                 'id': 0,
39156                 fields: ['value', 'text'],
39157                 data : d
39158             });
39159             this.valueField = 'value';
39160             this.displayField = 'text';
39161         }
39162         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39163         if(!this.lazyRender){
39164             this.target = true;
39165             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39166             s.parentNode.removeChild(s); // remove it
39167             this.render(this.el.parentNode);
39168         }else{
39169             s.parentNode.removeChild(s); // remove it
39170         }
39171
39172     }
39173     if (this.store) {
39174         this.store = Roo.factory(this.store, Roo.data);
39175     }
39176     
39177     this.selectedIndex = -1;
39178     if(this.mode == 'local'){
39179         if(config.queryDelay === undefined){
39180             this.queryDelay = 10;
39181         }
39182         if(config.minChars === undefined){
39183             this.minChars = 0;
39184         }
39185     }
39186 };
39187
39188 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39189     /**
39190      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39191      */
39192     /**
39193      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39194      * rendering into an Roo.Editor, defaults to false)
39195      */
39196     /**
39197      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39198      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39199      */
39200     /**
39201      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39202      */
39203     /**
39204      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39205      * the dropdown list (defaults to undefined, with no header element)
39206      */
39207
39208      /**
39209      * @cfg {String/Roo.Template} tpl The template to use to render the output
39210      */
39211      
39212     // private
39213     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39214     /**
39215      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39216      */
39217     listWidth: undefined,
39218     /**
39219      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39220      * mode = 'remote' or 'text' if mode = 'local')
39221      */
39222     displayField: undefined,
39223     /**
39224      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39225      * mode = 'remote' or 'value' if mode = 'local'). 
39226      * Note: use of a valueField requires the user make a selection
39227      * in order for a value to be mapped.
39228      */
39229     valueField: undefined,
39230     
39231     
39232     /**
39233      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39234      * field's data value (defaults to the underlying DOM element's name)
39235      */
39236     hiddenName: undefined,
39237     /**
39238      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39239      */
39240     listClass: '',
39241     /**
39242      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39243      */
39244     selectedClass: 'x-combo-selected',
39245     /**
39246      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39247      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39248      * which displays a downward arrow icon).
39249      */
39250     triggerClass : 'x-form-arrow-trigger',
39251     /**
39252      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39253      */
39254     shadow:'sides',
39255     /**
39256      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39257      * anchor positions (defaults to 'tl-bl')
39258      */
39259     listAlign: 'tl-bl?',
39260     /**
39261      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39262      */
39263     maxHeight: 300,
39264     /**
39265      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39266      * query specified by the allQuery config option (defaults to 'query')
39267      */
39268     triggerAction: 'query',
39269     /**
39270      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39271      * (defaults to 4, does not apply if editable = false)
39272      */
39273     minChars : 4,
39274     /**
39275      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39276      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39277      */
39278     typeAhead: false,
39279     /**
39280      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39281      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39282      */
39283     queryDelay: 500,
39284     /**
39285      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39286      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39287      */
39288     pageSize: 0,
39289     /**
39290      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39291      * when editable = true (defaults to false)
39292      */
39293     selectOnFocus:false,
39294     /**
39295      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39296      */
39297     queryParam: 'query',
39298     /**
39299      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39300      * when mode = 'remote' (defaults to 'Loading...')
39301      */
39302     loadingText: 'Loading...',
39303     /**
39304      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39305      */
39306     resizable: false,
39307     /**
39308      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39309      */
39310     handleHeight : 8,
39311     /**
39312      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39313      * traditional select (defaults to true)
39314      */
39315     editable: true,
39316     /**
39317      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39318      */
39319     allQuery: '',
39320     /**
39321      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39322      */
39323     mode: 'remote',
39324     /**
39325      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39326      * listWidth has a higher value)
39327      */
39328     minListWidth : 70,
39329     /**
39330      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39331      * allow the user to set arbitrary text into the field (defaults to false)
39332      */
39333     forceSelection:false,
39334     /**
39335      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39336      * if typeAhead = true (defaults to 250)
39337      */
39338     typeAheadDelay : 250,
39339     /**
39340      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39341      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39342      */
39343     valueNotFoundText : undefined,
39344     /**
39345      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39346      */
39347     blockFocus : false,
39348     
39349     /**
39350      * @cfg {Boolean} disableClear Disable showing of clear button.
39351      */
39352     disableClear : false,
39353     /**
39354      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39355      */
39356     alwaysQuery : false,
39357     
39358     //private
39359     addicon : false,
39360     editicon: false,
39361     
39362     // element that contains real text value.. (when hidden is used..)
39363      
39364     // private
39365     onRender : function(ct, position){
39366         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39367         if(this.hiddenName){
39368             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39369                     'before', true);
39370             this.hiddenField.value =
39371                 this.hiddenValue !== undefined ? this.hiddenValue :
39372                 this.value !== undefined ? this.value : '';
39373
39374             // prevent input submission
39375             this.el.dom.removeAttribute('name');
39376              
39377              
39378         }
39379         if(Roo.isGecko){
39380             this.el.dom.setAttribute('autocomplete', 'off');
39381         }
39382
39383         var cls = 'x-combo-list';
39384
39385         this.list = new Roo.Layer({
39386             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39387         });
39388
39389         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39390         this.list.setWidth(lw);
39391         this.list.swallowEvent('mousewheel');
39392         this.assetHeight = 0;
39393
39394         if(this.title){
39395             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39396             this.assetHeight += this.header.getHeight();
39397         }
39398
39399         this.innerList = this.list.createChild({cls:cls+'-inner'});
39400         this.innerList.on('mouseover', this.onViewOver, this);
39401         this.innerList.on('mousemove', this.onViewMove, this);
39402         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39403         
39404         if(this.allowBlank && !this.pageSize && !this.disableClear){
39405             this.footer = this.list.createChild({cls:cls+'-ft'});
39406             this.pageTb = new Roo.Toolbar(this.footer);
39407            
39408         }
39409         if(this.pageSize){
39410             this.footer = this.list.createChild({cls:cls+'-ft'});
39411             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39412                     {pageSize: this.pageSize});
39413             
39414         }
39415         
39416         if (this.pageTb && this.allowBlank && !this.disableClear) {
39417             var _this = this;
39418             this.pageTb.add(new Roo.Toolbar.Fill(), {
39419                 cls: 'x-btn-icon x-btn-clear',
39420                 text: '&#160;',
39421                 handler: function()
39422                 {
39423                     _this.collapse();
39424                     _this.clearValue();
39425                     _this.onSelect(false, -1);
39426                 }
39427             });
39428         }
39429         if (this.footer) {
39430             this.assetHeight += this.footer.getHeight();
39431         }
39432         
39433
39434         if(!this.tpl){
39435             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39436         }
39437
39438         this.view = new Roo.View(this.innerList, this.tpl, {
39439             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39440         });
39441
39442         this.view.on('click', this.onViewClick, this);
39443
39444         this.store.on('beforeload', this.onBeforeLoad, this);
39445         this.store.on('load', this.onLoad, this);
39446         this.store.on('loadexception', this.onLoadException, this);
39447
39448         if(this.resizable){
39449             this.resizer = new Roo.Resizable(this.list,  {
39450                pinned:true, handles:'se'
39451             });
39452             this.resizer.on('resize', function(r, w, h){
39453                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39454                 this.listWidth = w;
39455                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39456                 this.restrictHeight();
39457             }, this);
39458             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39459         }
39460         if(!this.editable){
39461             this.editable = true;
39462             this.setEditable(false);
39463         }  
39464         
39465         
39466         if (typeof(this.events.add.listeners) != 'undefined') {
39467             
39468             this.addicon = this.wrap.createChild(
39469                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39470        
39471             this.addicon.on('click', function(e) {
39472                 this.fireEvent('add', this);
39473             }, this);
39474         }
39475         if (typeof(this.events.edit.listeners) != 'undefined') {
39476             
39477             this.editicon = this.wrap.createChild(
39478                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39479             if (this.addicon) {
39480                 this.editicon.setStyle('margin-left', '40px');
39481             }
39482             this.editicon.on('click', function(e) {
39483                 
39484                 // we fire even  if inothing is selected..
39485                 this.fireEvent('edit', this, this.lastData );
39486                 
39487             }, this);
39488         }
39489         
39490         
39491         
39492     },
39493
39494     // private
39495     initEvents : function(){
39496         Roo.form.ComboBox.superclass.initEvents.call(this);
39497
39498         this.keyNav = new Roo.KeyNav(this.el, {
39499             "up" : function(e){
39500                 this.inKeyMode = true;
39501                 this.selectPrev();
39502             },
39503
39504             "down" : function(e){
39505                 if(!this.isExpanded()){
39506                     this.onTriggerClick();
39507                 }else{
39508                     this.inKeyMode = true;
39509                     this.selectNext();
39510                 }
39511             },
39512
39513             "enter" : function(e){
39514                 this.onViewClick();
39515                 //return true;
39516             },
39517
39518             "esc" : function(e){
39519                 this.collapse();
39520             },
39521
39522             "tab" : function(e){
39523                 this.onViewClick(false);
39524                 this.fireEvent("specialkey", this, e);
39525                 return true;
39526             },
39527
39528             scope : this,
39529
39530             doRelay : function(foo, bar, hname){
39531                 if(hname == 'down' || this.scope.isExpanded()){
39532                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39533                 }
39534                 return true;
39535             },
39536
39537             forceKeyDown: true
39538         });
39539         this.queryDelay = Math.max(this.queryDelay || 10,
39540                 this.mode == 'local' ? 10 : 250);
39541         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39542         if(this.typeAhead){
39543             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39544         }
39545         if(this.editable !== false){
39546             this.el.on("keyup", this.onKeyUp, this);
39547         }
39548         if(this.forceSelection){
39549             this.on('blur', this.doForce, this);
39550         }
39551     },
39552
39553     onDestroy : function(){
39554         if(this.view){
39555             this.view.setStore(null);
39556             this.view.el.removeAllListeners();
39557             this.view.el.remove();
39558             this.view.purgeListeners();
39559         }
39560         if(this.list){
39561             this.list.destroy();
39562         }
39563         if(this.store){
39564             this.store.un('beforeload', this.onBeforeLoad, this);
39565             this.store.un('load', this.onLoad, this);
39566             this.store.un('loadexception', this.onLoadException, this);
39567         }
39568         Roo.form.ComboBox.superclass.onDestroy.call(this);
39569     },
39570
39571     // private
39572     fireKey : function(e){
39573         if(e.isNavKeyPress() && !this.list.isVisible()){
39574             this.fireEvent("specialkey", this, e);
39575         }
39576     },
39577
39578     // private
39579     onResize: function(w, h){
39580         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39581         
39582         if(typeof w != 'number'){
39583             // we do not handle it!?!?
39584             return;
39585         }
39586         var tw = this.trigger.getWidth();
39587         tw += this.addicon ? this.addicon.getWidth() : 0;
39588         tw += this.editicon ? this.editicon.getWidth() : 0;
39589         var x = w - tw;
39590         this.el.setWidth( this.adjustWidth('input', x));
39591             
39592         this.trigger.setStyle('left', x+'px');
39593         
39594         if(this.list && this.listWidth === undefined){
39595             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39596             this.list.setWidth(lw);
39597             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39598         }
39599         
39600     
39601         
39602     },
39603
39604     /**
39605      * Allow or prevent the user from directly editing the field text.  If false is passed,
39606      * the user will only be able to select from the items defined in the dropdown list.  This method
39607      * is the runtime equivalent of setting the 'editable' config option at config time.
39608      * @param {Boolean} value True to allow the user to directly edit the field text
39609      */
39610     setEditable : function(value){
39611         if(value == this.editable){
39612             return;
39613         }
39614         this.editable = value;
39615         if(!value){
39616             this.el.dom.setAttribute('readOnly', true);
39617             this.el.on('mousedown', this.onTriggerClick,  this);
39618             this.el.addClass('x-combo-noedit');
39619         }else{
39620             this.el.dom.setAttribute('readOnly', false);
39621             this.el.un('mousedown', this.onTriggerClick,  this);
39622             this.el.removeClass('x-combo-noedit');
39623         }
39624     },
39625
39626     // private
39627     onBeforeLoad : function(){
39628         if(!this.hasFocus){
39629             return;
39630         }
39631         this.innerList.update(this.loadingText ?
39632                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39633         this.restrictHeight();
39634         this.selectedIndex = -1;
39635     },
39636
39637     // private
39638     onLoad : function(){
39639         if(!this.hasFocus){
39640             return;
39641         }
39642         if(this.store.getCount() > 0){
39643             this.expand();
39644             this.restrictHeight();
39645             if(this.lastQuery == this.allQuery){
39646                 if(this.editable){
39647                     this.el.dom.select();
39648                 }
39649                 if(!this.selectByValue(this.value, true)){
39650                     this.select(0, true);
39651                 }
39652             }else{
39653                 this.selectNext();
39654                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39655                     this.taTask.delay(this.typeAheadDelay);
39656                 }
39657             }
39658         }else{
39659             this.onEmptyResults();
39660         }
39661         //this.el.focus();
39662     },
39663     // private
39664     onLoadException : function()
39665     {
39666         this.collapse();
39667         Roo.log(this.store.reader.jsonData);
39668         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39669             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39670         }
39671         
39672         
39673     },
39674     // private
39675     onTypeAhead : function(){
39676         if(this.store.getCount() > 0){
39677             var r = this.store.getAt(0);
39678             var newValue = r.data[this.displayField];
39679             var len = newValue.length;
39680             var selStart = this.getRawValue().length;
39681             if(selStart != len){
39682                 this.setRawValue(newValue);
39683                 this.selectText(selStart, newValue.length);
39684             }
39685         }
39686     },
39687
39688     // private
39689     onSelect : function(record, index){
39690         if(this.fireEvent('beforeselect', this, record, index) !== false){
39691             this.setFromData(index > -1 ? record.data : false);
39692             this.collapse();
39693             this.fireEvent('select', this, record, index);
39694         }
39695     },
39696
39697     /**
39698      * Returns the currently selected field value or empty string if no value is set.
39699      * @return {String} value The selected value
39700      */
39701     getValue : function(){
39702         if(this.valueField){
39703             return typeof this.value != 'undefined' ? this.value : '';
39704         }else{
39705             return Roo.form.ComboBox.superclass.getValue.call(this);
39706         }
39707     },
39708
39709     /**
39710      * Clears any text/value currently set in the field
39711      */
39712     clearValue : function(){
39713         if(this.hiddenField){
39714             this.hiddenField.value = '';
39715         }
39716         this.value = '';
39717         this.setRawValue('');
39718         this.lastSelectionText = '';
39719         
39720     },
39721
39722     /**
39723      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39724      * will be displayed in the field.  If the value does not match the data value of an existing item,
39725      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39726      * Otherwise the field will be blank (although the value will still be set).
39727      * @param {String} value The value to match
39728      */
39729     setValue : function(v){
39730         var text = v;
39731         if(this.valueField){
39732             var r = this.findRecord(this.valueField, v);
39733             if(r){
39734                 text = r.data[this.displayField];
39735             }else if(this.valueNotFoundText !== undefined){
39736                 text = this.valueNotFoundText;
39737             }
39738         }
39739         this.lastSelectionText = text;
39740         if(this.hiddenField){
39741             this.hiddenField.value = v;
39742         }
39743         Roo.form.ComboBox.superclass.setValue.call(this, text);
39744         this.value = v;
39745     },
39746     /**
39747      * @property {Object} the last set data for the element
39748      */
39749     
39750     lastData : false,
39751     /**
39752      * Sets the value of the field based on a object which is related to the record format for the store.
39753      * @param {Object} value the value to set as. or false on reset?
39754      */
39755     setFromData : function(o){
39756         var dv = ''; // display value
39757         var vv = ''; // value value..
39758         this.lastData = o;
39759         if (this.displayField) {
39760             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39761         } else {
39762             // this is an error condition!!!
39763             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39764         }
39765         
39766         if(this.valueField){
39767             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39768         }
39769         if(this.hiddenField){
39770             this.hiddenField.value = vv;
39771             
39772             this.lastSelectionText = dv;
39773             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39774             this.value = vv;
39775             return;
39776         }
39777         // no hidden field.. - we store the value in 'value', but still display
39778         // display field!!!!
39779         this.lastSelectionText = dv;
39780         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39781         this.value = vv;
39782         
39783         
39784     },
39785     // private
39786     reset : function(){
39787         // overridden so that last data is reset..
39788         this.setValue(this.resetValue);
39789         this.clearInvalid();
39790         this.lastData = false;
39791         if (this.view) {
39792             this.view.clearSelections();
39793         }
39794     },
39795     // private
39796     findRecord : function(prop, value){
39797         var record;
39798         if(this.store.getCount() > 0){
39799             this.store.each(function(r){
39800                 if(r.data[prop] == value){
39801                     record = r;
39802                     return false;
39803                 }
39804                 return true;
39805             });
39806         }
39807         return record;
39808     },
39809     
39810     getName: function()
39811     {
39812         // returns hidden if it's set..
39813         if (!this.rendered) {return ''};
39814         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39815         
39816     },
39817     // private
39818     onViewMove : function(e, t){
39819         this.inKeyMode = false;
39820     },
39821
39822     // private
39823     onViewOver : function(e, t){
39824         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39825             return;
39826         }
39827         var item = this.view.findItemFromChild(t);
39828         if(item){
39829             var index = this.view.indexOf(item);
39830             this.select(index, false);
39831         }
39832     },
39833
39834     // private
39835     onViewClick : function(doFocus)
39836     {
39837         var index = this.view.getSelectedIndexes()[0];
39838         var r = this.store.getAt(index);
39839         if(r){
39840             this.onSelect(r, index);
39841         }
39842         if(doFocus !== false && !this.blockFocus){
39843             this.el.focus();
39844         }
39845     },
39846
39847     // private
39848     restrictHeight : function(){
39849         this.innerList.dom.style.height = '';
39850         var inner = this.innerList.dom;
39851         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39852         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39853         this.list.beginUpdate();
39854         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39855         this.list.alignTo(this.el, this.listAlign);
39856         this.list.endUpdate();
39857     },
39858
39859     // private
39860     onEmptyResults : function(){
39861         this.collapse();
39862     },
39863
39864     /**
39865      * Returns true if the dropdown list is expanded, else false.
39866      */
39867     isExpanded : function(){
39868         return this.list.isVisible();
39869     },
39870
39871     /**
39872      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39873      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39874      * @param {String} value The data value of the item to select
39875      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39876      * selected item if it is not currently in view (defaults to true)
39877      * @return {Boolean} True if the value matched an item in the list, else false
39878      */
39879     selectByValue : function(v, scrollIntoView){
39880         if(v !== undefined && v !== null){
39881             var r = this.findRecord(this.valueField || this.displayField, v);
39882             if(r){
39883                 this.select(this.store.indexOf(r), scrollIntoView);
39884                 return true;
39885             }
39886         }
39887         return false;
39888     },
39889
39890     /**
39891      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39892      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39893      * @param {Number} index The zero-based index of the list item to select
39894      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39895      * selected item if it is not currently in view (defaults to true)
39896      */
39897     select : function(index, scrollIntoView){
39898         this.selectedIndex = index;
39899         this.view.select(index);
39900         if(scrollIntoView !== false){
39901             var el = this.view.getNode(index);
39902             if(el){
39903                 this.innerList.scrollChildIntoView(el, false);
39904             }
39905         }
39906     },
39907
39908     // private
39909     selectNext : function(){
39910         var ct = this.store.getCount();
39911         if(ct > 0){
39912             if(this.selectedIndex == -1){
39913                 this.select(0);
39914             }else if(this.selectedIndex < ct-1){
39915                 this.select(this.selectedIndex+1);
39916             }
39917         }
39918     },
39919
39920     // private
39921     selectPrev : function(){
39922         var ct = this.store.getCount();
39923         if(ct > 0){
39924             if(this.selectedIndex == -1){
39925                 this.select(0);
39926             }else if(this.selectedIndex != 0){
39927                 this.select(this.selectedIndex-1);
39928             }
39929         }
39930     },
39931
39932     // private
39933     onKeyUp : function(e){
39934         if(this.editable !== false && !e.isSpecialKey()){
39935             this.lastKey = e.getKey();
39936             this.dqTask.delay(this.queryDelay);
39937         }
39938     },
39939
39940     // private
39941     validateBlur : function(){
39942         return !this.list || !this.list.isVisible();   
39943     },
39944
39945     // private
39946     initQuery : function(){
39947         this.doQuery(this.getRawValue());
39948     },
39949
39950     // private
39951     doForce : function(){
39952         if(this.el.dom.value.length > 0){
39953             this.el.dom.value =
39954                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39955              
39956         }
39957     },
39958
39959     /**
39960      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39961      * query allowing the query action to be canceled if needed.
39962      * @param {String} query The SQL query to execute
39963      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39964      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39965      * saved in the current store (defaults to false)
39966      */
39967     doQuery : function(q, forceAll){
39968         if(q === undefined || q === null){
39969             q = '';
39970         }
39971         var qe = {
39972             query: q,
39973             forceAll: forceAll,
39974             combo: this,
39975             cancel:false
39976         };
39977         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39978             return false;
39979         }
39980         q = qe.query;
39981         forceAll = qe.forceAll;
39982         if(forceAll === true || (q.length >= this.minChars)){
39983             if(this.lastQuery != q || this.alwaysQuery){
39984                 this.lastQuery = q;
39985                 if(this.mode == 'local'){
39986                     this.selectedIndex = -1;
39987                     if(forceAll){
39988                         this.store.clearFilter();
39989                     }else{
39990                         this.store.filter(this.displayField, q);
39991                     }
39992                     this.onLoad();
39993                 }else{
39994                     this.store.baseParams[this.queryParam] = q;
39995                     this.store.load({
39996                         params: this.getParams(q)
39997                     });
39998                     this.expand();
39999                 }
40000             }else{
40001                 this.selectedIndex = -1;
40002                 this.onLoad();   
40003             }
40004         }
40005     },
40006
40007     // private
40008     getParams : function(q){
40009         var p = {};
40010         //p[this.queryParam] = q;
40011         if(this.pageSize){
40012             p.start = 0;
40013             p.limit = this.pageSize;
40014         }
40015         return p;
40016     },
40017
40018     /**
40019      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40020      */
40021     collapse : function(){
40022         if(!this.isExpanded()){
40023             return;
40024         }
40025         this.list.hide();
40026         Roo.get(document).un('mousedown', this.collapseIf, this);
40027         Roo.get(document).un('mousewheel', this.collapseIf, this);
40028         if (!this.editable) {
40029             Roo.get(document).un('keydown', this.listKeyPress, this);
40030         }
40031         this.fireEvent('collapse', this);
40032     },
40033
40034     // private
40035     collapseIf : function(e){
40036         if(!e.within(this.wrap) && !e.within(this.list)){
40037             this.collapse();
40038         }
40039     },
40040
40041     /**
40042      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40043      */
40044     expand : function(){
40045         if(this.isExpanded() || !this.hasFocus){
40046             return;
40047         }
40048         this.list.alignTo(this.el, this.listAlign);
40049         this.list.show();
40050         Roo.get(document).on('mousedown', this.collapseIf, this);
40051         Roo.get(document).on('mousewheel', this.collapseIf, this);
40052         if (!this.editable) {
40053             Roo.get(document).on('keydown', this.listKeyPress, this);
40054         }
40055         
40056         this.fireEvent('expand', this);
40057     },
40058
40059     // private
40060     // Implements the default empty TriggerField.onTriggerClick function
40061     onTriggerClick : function(){
40062         if(this.disabled){
40063             return;
40064         }
40065         if(this.isExpanded()){
40066             this.collapse();
40067             if (!this.blockFocus) {
40068                 this.el.focus();
40069             }
40070             
40071         }else {
40072             this.hasFocus = true;
40073             if(this.triggerAction == 'all') {
40074                 this.doQuery(this.allQuery, true);
40075             } else {
40076                 this.doQuery(this.getRawValue());
40077             }
40078             if (!this.blockFocus) {
40079                 this.el.focus();
40080             }
40081         }
40082     },
40083     listKeyPress : function(e)
40084     {
40085         //Roo.log('listkeypress');
40086         // scroll to first matching element based on key pres..
40087         if (e.isSpecialKey()) {
40088             return false;
40089         }
40090         var k = String.fromCharCode(e.getKey()).toUpperCase();
40091         //Roo.log(k);
40092         var match  = false;
40093         var csel = this.view.getSelectedNodes();
40094         var cselitem = false;
40095         if (csel.length) {
40096             var ix = this.view.indexOf(csel[0]);
40097             cselitem  = this.store.getAt(ix);
40098             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40099                 cselitem = false;
40100             }
40101             
40102         }
40103         
40104         this.store.each(function(v) { 
40105             if (cselitem) {
40106                 // start at existing selection.
40107                 if (cselitem.id == v.id) {
40108                     cselitem = false;
40109                 }
40110                 return;
40111             }
40112                 
40113             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40114                 match = this.store.indexOf(v);
40115                 return false;
40116             }
40117         }, this);
40118         
40119         if (match === false) {
40120             return true; // no more action?
40121         }
40122         // scroll to?
40123         this.view.select(match);
40124         var sn = Roo.get(this.view.getSelectedNodes()[0])
40125         sn.scrollIntoView(sn.dom.parentNode, false);
40126     }
40127
40128     /** 
40129     * @cfg {Boolean} grow 
40130     * @hide 
40131     */
40132     /** 
40133     * @cfg {Number} growMin 
40134     * @hide 
40135     */
40136     /** 
40137     * @cfg {Number} growMax 
40138     * @hide 
40139     */
40140     /**
40141      * @hide
40142      * @method autoSize
40143      */
40144 });/*
40145  * Copyright(c) 2010-2012, Roo J Solutions Limited
40146  *
40147  * Licence LGPL
40148  *
40149  */
40150
40151 /**
40152  * @class Roo.form.ComboBoxArray
40153  * @extends Roo.form.TextField
40154  * A facebook style adder... for lists of email / people / countries  etc...
40155  * pick multiple items from a combo box, and shows each one.
40156  *
40157  *  Fred [x]  Brian [x]  [Pick another |v]
40158  *
40159  *
40160  *  For this to work: it needs various extra information
40161  *    - normal combo problay has
40162  *      name, hiddenName
40163  *    + displayField, valueField
40164  *
40165  *    For our purpose...
40166  *
40167  *
40168  *   If we change from 'extends' to wrapping...
40169  *   
40170  *  
40171  *
40172  
40173  
40174  * @constructor
40175  * Create a new ComboBoxArray.
40176  * @param {Object} config Configuration options
40177  */
40178  
40179
40180 Roo.form.ComboBoxArray = function(config)
40181 {
40182     this.addEvents({
40183         /**
40184          * @event remove
40185          * Fires when remove the value from the list
40186              * @param {Roo.form.ComboBoxArray} _self This combo box array
40187              * @param {Roo.form.ComboBoxArray.Item} item removed item
40188              */
40189         'remove' : true
40190         
40191         
40192     });
40193     
40194     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40195     
40196     this.items = new Roo.util.MixedCollection(false);
40197     
40198     // construct the child combo...
40199     
40200     
40201     
40202     
40203    
40204     
40205 }
40206
40207  
40208 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40209
40210     /**
40211      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40212      */
40213     
40214     lastData : false,
40215     
40216     // behavies liek a hiddne field
40217     inputType:      'hidden',
40218     /**
40219      * @cfg {Number} width The width of the box that displays the selected element
40220      */ 
40221     width:          300,
40222
40223     
40224     
40225     /**
40226      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40227      */
40228     name : false,
40229     /**
40230      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40231      */
40232     hiddenName : false,
40233     
40234     
40235     // private the array of items that are displayed..
40236     items  : false,
40237     // private - the hidden field el.
40238     hiddenEl : false,
40239     // private - the filed el..
40240     el : false,
40241     
40242     //validateValue : function() { return true; }, // all values are ok!
40243     //onAddClick: function() { },
40244     
40245     onRender : function(ct, position) 
40246     {
40247         
40248         // create the standard hidden element
40249         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40250         
40251         
40252         // give fake names to child combo;
40253         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40254         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40255         
40256         this.combo = Roo.factory(this.combo, Roo.form);
40257         this.combo.onRender(ct, position);
40258         if (typeof(this.combo.width) != 'undefined') {
40259             this.combo.onResize(this.combo.width,0);
40260         }
40261         
40262         this.combo.initEvents();
40263         
40264         // assigned so form know we need to do this..
40265         this.store          = this.combo.store;
40266         this.valueField     = this.combo.valueField;
40267         this.displayField   = this.combo.displayField ;
40268         
40269         
40270         this.combo.wrap.addClass('x-cbarray-grp');
40271         
40272         var cbwrap = this.combo.wrap.createChild(
40273             {tag: 'div', cls: 'x-cbarray-cb'},
40274             this.combo.el.dom
40275         );
40276         
40277              
40278         this.hiddenEl = this.combo.wrap.createChild({
40279             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40280         });
40281         this.el = this.combo.wrap.createChild({
40282             tag: 'input',  type:'hidden' , name: this.name, value : ''
40283         });
40284          //   this.el.dom.removeAttribute("name");
40285         
40286         
40287         this.outerWrap = this.combo.wrap;
40288         this.wrap = cbwrap;
40289         
40290         this.outerWrap.setWidth(this.width);
40291         this.outerWrap.dom.removeChild(this.el.dom);
40292         
40293         this.wrap.dom.appendChild(this.el.dom);
40294         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40295         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40296         
40297         this.combo.trigger.setStyle('position','relative');
40298         this.combo.trigger.setStyle('left', '0px');
40299         this.combo.trigger.setStyle('top', '2px');
40300         
40301         this.combo.el.setStyle('vertical-align', 'text-bottom');
40302         
40303         //this.trigger.setStyle('vertical-align', 'top');
40304         
40305         // this should use the code from combo really... on('add' ....)
40306         if (this.adder) {
40307             
40308         
40309             this.adder = this.outerWrap.createChild(
40310                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40311             var _t = this;
40312             this.adder.on('click', function(e) {
40313                 _t.fireEvent('adderclick', this, e);
40314             }, _t);
40315         }
40316         //var _t = this;
40317         //this.adder.on('click', this.onAddClick, _t);
40318         
40319         
40320         this.combo.on('select', function(cb, rec, ix) {
40321             this.addItem(rec.data);
40322             
40323             cb.setValue('');
40324             cb.el.dom.value = '';
40325             //cb.lastData = rec.data;
40326             // add to list
40327             
40328         }, this);
40329         
40330         
40331     },
40332     
40333     
40334     getName: function()
40335     {
40336         // returns hidden if it's set..
40337         if (!this.rendered) {return ''};
40338         return  this.hiddenName ? this.hiddenName : this.name;
40339         
40340     },
40341     
40342     
40343     onResize: function(w, h){
40344         
40345         return;
40346         // not sure if this is needed..
40347         //this.combo.onResize(w,h);
40348         
40349         if(typeof w != 'number'){
40350             // we do not handle it!?!?
40351             return;
40352         }
40353         var tw = this.combo.trigger.getWidth();
40354         tw += this.addicon ? this.addicon.getWidth() : 0;
40355         tw += this.editicon ? this.editicon.getWidth() : 0;
40356         var x = w - tw;
40357         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40358             
40359         this.combo.trigger.setStyle('left', '0px');
40360         
40361         if(this.list && this.listWidth === undefined){
40362             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40363             this.list.setWidth(lw);
40364             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40365         }
40366         
40367     
40368         
40369     },
40370     
40371     addItem: function(rec)
40372     {
40373         var valueField = this.combo.valueField;
40374         var displayField = this.combo.displayField;
40375         if (this.items.indexOfKey(rec[valueField]) > -1) {
40376             //console.log("GOT " + rec.data.id);
40377             return;
40378         }
40379         
40380         var x = new Roo.form.ComboBoxArray.Item({
40381             //id : rec[this.idField],
40382             data : rec,
40383             displayField : displayField ,
40384             tipField : displayField ,
40385             cb : this
40386         });
40387         // use the 
40388         this.items.add(rec[valueField],x);
40389         // add it before the element..
40390         this.updateHiddenEl();
40391         x.render(this.outerWrap, this.wrap.dom);
40392         // add the image handler..
40393     },
40394     
40395     updateHiddenEl : function()
40396     {
40397         this.validate();
40398         if (!this.hiddenEl) {
40399             return;
40400         }
40401         var ar = [];
40402         var idField = this.combo.valueField;
40403         
40404         this.items.each(function(f) {
40405             ar.push(f.data[idField]);
40406            
40407         });
40408         this.hiddenEl.dom.value = ar.join(',');
40409         this.validate();
40410     },
40411     
40412     reset : function()
40413     {
40414         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40415         this.items.each(function(f) {
40416            f.remove(); 
40417         });
40418         this.el.dom.value = '';
40419         if (this.hiddenEl) {
40420             this.hiddenEl.dom.value = '';
40421         }
40422         
40423     },
40424     getValue: function()
40425     {
40426         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40427     },
40428     setValue: function(v) // not a valid action - must use addItems..
40429     {
40430          
40431         this.reset();
40432         
40433         
40434         
40435         if (this.store.isLocal && (typeof(v) == 'string')) {
40436             // then we can use the store to find the values..
40437             // comma seperated at present.. this needs to allow JSON based encoding..
40438             this.hiddenEl.value  = v;
40439             var v_ar = [];
40440             Roo.each(v.split(','), function(k) {
40441                 Roo.log("CHECK " + this.valueField + ',' + k);
40442                 var li = this.store.query(this.valueField, k);
40443                 if (!li.length) {
40444                     return;
40445                 }
40446                 var add = {};
40447                 add[this.valueField] = k;
40448                 add[this.displayField] = li.item(0).data[this.displayField];
40449                 
40450                 this.addItem(add);
40451             }, this) 
40452              
40453         }
40454         if (typeof(v) == 'object') {
40455             // then let's assume it's an array of objects..
40456             Roo.each(v, function(l) {
40457                 this.addItem(l);
40458             }, this);
40459              
40460         }
40461         
40462         
40463     },
40464     setFromData: function(v)
40465     {
40466         // this recieves an object, if setValues is called.
40467         this.reset();
40468         this.el.dom.value = v[this.displayField];
40469         this.hiddenEl.dom.value = v[this.valueField];
40470         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40471             return;
40472         }
40473         var kv = v[this.valueField];
40474         var dv = v[this.displayField];
40475         kv = typeof(kv) != 'string' ? '' : kv;
40476         dv = typeof(dv) != 'string' ? '' : dv;
40477         
40478         
40479         var keys = kv.split(',');
40480         var display = dv.split(',');
40481         for (var i = 0 ; i < keys.length; i++) {
40482             
40483             add = {};
40484             add[this.valueField] = keys[i];
40485             add[this.displayField] = display[i];
40486             this.addItem(add);
40487         }
40488       
40489         
40490     },
40491     
40492     /**
40493      * Validates the combox array value
40494      * @return {Boolean} True if the value is valid, else false
40495      */
40496     validate : function(){
40497         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40498             this.clearInvalid();
40499             return true;
40500         }
40501         return false;
40502     },
40503     
40504     validateValue : function(value){
40505         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40506         
40507     },
40508     
40509     /*@
40510      * overide
40511      * 
40512      */
40513     isDirty : function() {
40514         if(this.disabled) {
40515             return false;
40516         }
40517         
40518         try {
40519             var d = Roo.decode(String(this.originalValue));
40520         } catch (e) {
40521             return String(this.getValue()) !== String(this.originalValue);
40522         }
40523         
40524         var originalValue = [];
40525         
40526         for (var i = 0; i < d.length; i++){
40527             originalValue.push(d[i][this.valueField]);
40528         }
40529         
40530         return String(this.getValue()) !== String(originalValue.join(','));
40531         
40532     }
40533     
40534 });
40535
40536
40537
40538 /**
40539  * @class Roo.form.ComboBoxArray.Item
40540  * @extends Roo.BoxComponent
40541  * A selected item in the list
40542  *  Fred [x]  Brian [x]  [Pick another |v]
40543  * 
40544  * @constructor
40545  * Create a new item.
40546  * @param {Object} config Configuration options
40547  */
40548  
40549 Roo.form.ComboBoxArray.Item = function(config) {
40550     config.id = Roo.id();
40551     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40552 }
40553
40554 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40555     data : {},
40556     cb: false,
40557     displayField : false,
40558     tipField : false,
40559     
40560     
40561     defaultAutoCreate : {
40562         tag: 'div',
40563         cls: 'x-cbarray-item',
40564         cn : [ 
40565             { tag: 'div' },
40566             {
40567                 tag: 'img',
40568                 width:16,
40569                 height : 16,
40570                 src : Roo.BLANK_IMAGE_URL ,
40571                 align: 'center'
40572             }
40573         ]
40574         
40575     },
40576     
40577  
40578     onRender : function(ct, position)
40579     {
40580         Roo.form.Field.superclass.onRender.call(this, ct, position);
40581         
40582         if(!this.el){
40583             var cfg = this.getAutoCreate();
40584             this.el = ct.createChild(cfg, position);
40585         }
40586         
40587         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40588         
40589         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40590             this.cb.renderer(this.data) :
40591             String.format('{0}',this.data[this.displayField]);
40592         
40593             
40594         this.el.child('div').dom.setAttribute('qtip',
40595                         String.format('{0}',this.data[this.tipField])
40596         );
40597         
40598         this.el.child('img').on('click', this.remove, this);
40599         
40600     },
40601    
40602     remove : function()
40603     {
40604         this.cb.items.remove(this);
40605         this.el.child('img').un('click', this.remove, this);
40606         this.el.remove();
40607         this.cb.updateHiddenEl();
40608         
40609         this.cb.fireEvent('remove', this.cb, this);
40610     }
40611 });/*
40612  * Based on:
40613  * Ext JS Library 1.1.1
40614  * Copyright(c) 2006-2007, Ext JS, LLC.
40615  *
40616  * Originally Released Under LGPL - original licence link has changed is not relivant.
40617  *
40618  * Fork - LGPL
40619  * <script type="text/javascript">
40620  */
40621 /**
40622  * @class Roo.form.Checkbox
40623  * @extends Roo.form.Field
40624  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40625  * @constructor
40626  * Creates a new Checkbox
40627  * @param {Object} config Configuration options
40628  */
40629 Roo.form.Checkbox = function(config){
40630     Roo.form.Checkbox.superclass.constructor.call(this, config);
40631     this.addEvents({
40632         /**
40633          * @event check
40634          * Fires when the checkbox is checked or unchecked.
40635              * @param {Roo.form.Checkbox} this This checkbox
40636              * @param {Boolean} checked The new checked value
40637              */
40638         check : true
40639     });
40640 };
40641
40642 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40643     /**
40644      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40645      */
40646     focusClass : undefined,
40647     /**
40648      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40649      */
40650     fieldClass: "x-form-field",
40651     /**
40652      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40653      */
40654     checked: false,
40655     /**
40656      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40657      * {tag: "input", type: "checkbox", autocomplete: "off"})
40658      */
40659     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40660     /**
40661      * @cfg {String} boxLabel The text that appears beside the checkbox
40662      */
40663     boxLabel : "",
40664     /**
40665      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40666      */  
40667     inputValue : '1',
40668     /**
40669      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40670      */
40671      valueOff: '0', // value when not checked..
40672
40673     actionMode : 'viewEl', 
40674     //
40675     // private
40676     itemCls : 'x-menu-check-item x-form-item',
40677     groupClass : 'x-menu-group-item',
40678     inputType : 'hidden',
40679     
40680     
40681     inSetChecked: false, // check that we are not calling self...
40682     
40683     inputElement: false, // real input element?
40684     basedOn: false, // ????
40685     
40686     isFormField: true, // not sure where this is needed!!!!
40687
40688     onResize : function(){
40689         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40690         if(!this.boxLabel){
40691             this.el.alignTo(this.wrap, 'c-c');
40692         }
40693     },
40694
40695     initEvents : function(){
40696         Roo.form.Checkbox.superclass.initEvents.call(this);
40697         this.el.on("click", this.onClick,  this);
40698         this.el.on("change", this.onClick,  this);
40699     },
40700
40701
40702     getResizeEl : function(){
40703         return this.wrap;
40704     },
40705
40706     getPositionEl : function(){
40707         return this.wrap;
40708     },
40709
40710     // private
40711     onRender : function(ct, position){
40712         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40713         /*
40714         if(this.inputValue !== undefined){
40715             this.el.dom.value = this.inputValue;
40716         }
40717         */
40718         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40719         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40720         var viewEl = this.wrap.createChild({ 
40721             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40722         this.viewEl = viewEl;   
40723         this.wrap.on('click', this.onClick,  this); 
40724         
40725         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40726         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40727         
40728         
40729         
40730         if(this.boxLabel){
40731             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40732         //    viewEl.on('click', this.onClick,  this); 
40733         }
40734         //if(this.checked){
40735             this.setChecked(this.checked);
40736         //}else{
40737             //this.checked = this.el.dom;
40738         //}
40739
40740     },
40741
40742     // private
40743     initValue : Roo.emptyFn,
40744
40745     /**
40746      * Returns the checked state of the checkbox.
40747      * @return {Boolean} True if checked, else false
40748      */
40749     getValue : function(){
40750         if(this.el){
40751             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40752         }
40753         return this.valueOff;
40754         
40755     },
40756
40757         // private
40758     onClick : function(){ 
40759         this.setChecked(!this.checked);
40760
40761         //if(this.el.dom.checked != this.checked){
40762         //    this.setValue(this.el.dom.checked);
40763        // }
40764     },
40765
40766     /**
40767      * Sets the checked state of the checkbox.
40768      * On is always based on a string comparison between inputValue and the param.
40769      * @param {Boolean/String} value - the value to set 
40770      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40771      */
40772     setValue : function(v,suppressEvent){
40773         
40774         
40775         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40776         //if(this.el && this.el.dom){
40777         //    this.el.dom.checked = this.checked;
40778         //    this.el.dom.defaultChecked = this.checked;
40779         //}
40780         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40781         //this.fireEvent("check", this, this.checked);
40782     },
40783     // private..
40784     setChecked : function(state,suppressEvent)
40785     {
40786         if (this.inSetChecked) {
40787             this.checked = state;
40788             return;
40789         }
40790         
40791     
40792         if(this.wrap){
40793             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40794         }
40795         this.checked = state;
40796         if(suppressEvent !== true){
40797             this.fireEvent('check', this, state);
40798         }
40799         this.inSetChecked = true;
40800         this.el.dom.value = state ? this.inputValue : this.valueOff;
40801         this.inSetChecked = false;
40802         
40803     },
40804     // handle setting of hidden value by some other method!!?!?
40805     setFromHidden: function()
40806     {
40807         if(!this.el){
40808             return;
40809         }
40810         //console.log("SET FROM HIDDEN");
40811         //alert('setFrom hidden');
40812         this.setValue(this.el.dom.value);
40813     },
40814     
40815     onDestroy : function()
40816     {
40817         if(this.viewEl){
40818             Roo.get(this.viewEl).remove();
40819         }
40820          
40821         Roo.form.Checkbox.superclass.onDestroy.call(this);
40822     }
40823
40824 });/*
40825  * Based on:
40826  * Ext JS Library 1.1.1
40827  * Copyright(c) 2006-2007, Ext JS, LLC.
40828  *
40829  * Originally Released Under LGPL - original licence link has changed is not relivant.
40830  *
40831  * Fork - LGPL
40832  * <script type="text/javascript">
40833  */
40834  
40835 /**
40836  * @class Roo.form.Radio
40837  * @extends Roo.form.Checkbox
40838  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40839  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40840  * @constructor
40841  * Creates a new Radio
40842  * @param {Object} config Configuration options
40843  */
40844 Roo.form.Radio = function(){
40845     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40846 };
40847 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40848     inputType: 'radio',
40849
40850     /**
40851      * If this radio is part of a group, it will return the selected value
40852      * @return {String}
40853      */
40854     getGroupValue : function(){
40855         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40856     },
40857     
40858     
40859     onRender : function(ct, position){
40860         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40861         
40862         if(this.inputValue !== undefined){
40863             this.el.dom.value = this.inputValue;
40864         }
40865          
40866         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40867         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40868         //var viewEl = this.wrap.createChild({ 
40869         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40870         //this.viewEl = viewEl;   
40871         //this.wrap.on('click', this.onClick,  this); 
40872         
40873         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40874         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40875         
40876         
40877         
40878         if(this.boxLabel){
40879             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40880         //    viewEl.on('click', this.onClick,  this); 
40881         }
40882          if(this.checked){
40883             this.el.dom.checked =   'checked' ;
40884         }
40885          
40886     } 
40887     
40888     
40889 });//<script type="text/javascript">
40890
40891 /*
40892  * Based  Ext JS Library 1.1.1
40893  * Copyright(c) 2006-2007, Ext JS, LLC.
40894  * LGPL
40895  *
40896  */
40897  
40898 /**
40899  * @class Roo.HtmlEditorCore
40900  * @extends Roo.Component
40901  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
40902  *
40903  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40904  */
40905
40906 Roo.HtmlEditorCore = function(config){
40907     
40908     
40909     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
40910     this.addEvents({
40911         /**
40912          * @event initialize
40913          * Fires when the editor is fully initialized (including the iframe)
40914          * @param {Roo.HtmlEditorCore} this
40915          */
40916         initialize: true,
40917         /**
40918          * @event activate
40919          * Fires when the editor is first receives the focus. Any insertion must wait
40920          * until after this event.
40921          * @param {Roo.HtmlEditorCore} this
40922          */
40923         activate: true,
40924          /**
40925          * @event beforesync
40926          * Fires before the textarea is updated with content from the editor iframe. Return false
40927          * to cancel the sync.
40928          * @param {Roo.HtmlEditorCore} this
40929          * @param {String} html
40930          */
40931         beforesync: true,
40932          /**
40933          * @event beforepush
40934          * Fires before the iframe editor is updated with content from the textarea. Return false
40935          * to cancel the push.
40936          * @param {Roo.HtmlEditorCore} this
40937          * @param {String} html
40938          */
40939         beforepush: true,
40940          /**
40941          * @event sync
40942          * Fires when the textarea is updated with content from the editor iframe.
40943          * @param {Roo.HtmlEditorCore} this
40944          * @param {String} html
40945          */
40946         sync: true,
40947          /**
40948          * @event push
40949          * Fires when the iframe editor is updated with content from the textarea.
40950          * @param {Roo.HtmlEditorCore} this
40951          * @param {String} html
40952          */
40953         push: true,
40954         
40955         /**
40956          * @event editorevent
40957          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40958          * @param {Roo.HtmlEditorCore} this
40959          */
40960         editorevent: true
40961     });
40962      
40963 };
40964
40965
40966 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
40967
40968
40969      /**
40970      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
40971      */
40972     
40973     owner : false,
40974     
40975      /**
40976      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40977      *                        Roo.resizable.
40978      */
40979     resizable : false,
40980      /**
40981      * @cfg {Number} height (in pixels)
40982      */   
40983     height: 300,
40984    /**
40985      * @cfg {Number} width (in pixels)
40986      */   
40987     width: 500,
40988     
40989     /**
40990      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40991      * 
40992      */
40993     stylesheets: false,
40994     
40995     // id of frame..
40996     frameId: false,
40997     
40998     // private properties
40999     validationEvent : false,
41000     deferHeight: true,
41001     initialized : false,
41002     activated : false,
41003     sourceEditMode : false,
41004     onFocus : Roo.emptyFn,
41005     iframePad:3,
41006     hideMode:'offsets',
41007     
41008     clearUp: true,
41009     
41010      
41011     
41012
41013     /**
41014      * Protected method that will not generally be called directly. It
41015      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41016      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41017      */
41018     getDocMarkup : function(){
41019         // body styles..
41020         var st = '';
41021         Roo.log(this.stylesheets);
41022         
41023         // inherit styels from page...?? 
41024         if (this.stylesheets === false) {
41025             
41026             Roo.get(document.head).select('style').each(function(node) {
41027                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41028             });
41029             
41030             Roo.get(document.head).select('link').each(function(node) { 
41031                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41032             });
41033             
41034         } else if (!this.stylesheets.length) {
41035                 // simple..
41036                 st = '<style type="text/css">' +
41037                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41038                    '</style>';
41039         } else {
41040             Roo.each(this.stylesheets, function(s) {
41041                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41042             });
41043             
41044         }
41045         
41046         st +=  '<style type="text/css">' +
41047             'IMG { cursor: pointer } ' +
41048         '</style>';
41049
41050         
41051         return '<html><head>' + st  +
41052             //<style type="text/css">' +
41053             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41054             //'</style>' +
41055             ' </head><body class="roo-htmleditor-body"></body></html>';
41056     },
41057
41058     // private
41059     onRender : function(ct, position)
41060     {
41061         var _t = this;
41062         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41063         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41064         
41065         
41066         this.el.dom.style.border = '0 none';
41067         this.el.dom.setAttribute('tabIndex', -1);
41068         this.el.addClass('x-hidden hide');
41069         
41070         
41071         
41072         if(Roo.isIE){ // fix IE 1px bogus margin
41073             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41074         }
41075        
41076         
41077         this.frameId = Roo.id();
41078         
41079          
41080         
41081         var iframe = this.owner.wrap.createChild({
41082             tag: 'iframe',
41083             cls: 'form-control', // bootstrap..
41084             id: this.frameId,
41085             name: this.frameId,
41086             frameBorder : 'no',
41087             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41088         }, this.el
41089         );
41090         
41091         
41092         this.iframe = iframe.dom;
41093
41094          this.assignDocWin();
41095         
41096         this.doc.designMode = 'on';
41097        
41098         this.doc.open();
41099         this.doc.write(this.getDocMarkup());
41100         this.doc.close();
41101
41102         
41103         var task = { // must defer to wait for browser to be ready
41104             run : function(){
41105                 //console.log("run task?" + this.doc.readyState);
41106                 this.assignDocWin();
41107                 if(this.doc.body || this.doc.readyState == 'complete'){
41108                     try {
41109                         this.doc.designMode="on";
41110                     } catch (e) {
41111                         return;
41112                     }
41113                     Roo.TaskMgr.stop(task);
41114                     this.initEditor.defer(10, this);
41115                 }
41116             },
41117             interval : 10,
41118             duration: 10000,
41119             scope: this
41120         };
41121         Roo.TaskMgr.start(task);
41122
41123         
41124          
41125     },
41126
41127     // private
41128     onResize : function(w, h)
41129     {
41130          Roo.log('resize: ' +w + ',' + h );
41131         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41132         if(!this.iframe){
41133             return;
41134         }
41135         if(typeof w == 'number'){
41136             
41137             this.iframe.style.width = w + 'px';
41138         }
41139         if(typeof h == 'number'){
41140             
41141             this.iframe.style.height = h + 'px';
41142             if(this.doc){
41143                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41144             }
41145         }
41146         
41147     },
41148
41149     /**
41150      * Toggles the editor between standard and source edit mode.
41151      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41152      */
41153     toggleSourceEdit : function(sourceEditMode){
41154         
41155         this.sourceEditMode = sourceEditMode === true;
41156         
41157         if(this.sourceEditMode){
41158  
41159             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41160             
41161         }else{
41162             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41163             //this.iframe.className = '';
41164             this.deferFocus();
41165         }
41166         //this.setSize(this.owner.wrap.getSize());
41167         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41168     },
41169
41170     
41171   
41172
41173     /**
41174      * Protected method that will not generally be called directly. If you need/want
41175      * custom HTML cleanup, this is the method you should override.
41176      * @param {String} html The HTML to be cleaned
41177      * return {String} The cleaned HTML
41178      */
41179     cleanHtml : function(html){
41180         html = String(html);
41181         if(html.length > 5){
41182             if(Roo.isSafari){ // strip safari nonsense
41183                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41184             }
41185         }
41186         if(html == '&nbsp;'){
41187             html = '';
41188         }
41189         return html;
41190     },
41191
41192     /**
41193      * HTML Editor -> Textarea
41194      * Protected method that will not generally be called directly. Syncs the contents
41195      * of the editor iframe with the textarea.
41196      */
41197     syncValue : function(){
41198         if(this.initialized){
41199             var bd = (this.doc.body || this.doc.documentElement);
41200             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41201             var html = bd.innerHTML;
41202             if(Roo.isSafari){
41203                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41204                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41205                 if(m && m[1]){
41206                     html = '<div style="'+m[0]+'">' + html + '</div>';
41207                 }
41208             }
41209             html = this.cleanHtml(html);
41210             // fix up the special chars.. normaly like back quotes in word...
41211             // however we do not want to do this with chinese..
41212             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41213                 var cc = b.charCodeAt();
41214                 if (
41215                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41216                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41217                     (cc >= 0xf900 && cc < 0xfb00 )
41218                 ) {
41219                         return b;
41220                 }
41221                 return "&#"+cc+";" 
41222             });
41223             if(this.owner.fireEvent('beforesync', this, html) !== false){
41224                 this.el.dom.value = html;
41225                 this.owner.fireEvent('sync', this, html);
41226             }
41227         }
41228     },
41229
41230     /**
41231      * Protected method that will not generally be called directly. Pushes the value of the textarea
41232      * into the iframe editor.
41233      */
41234     pushValue : function(){
41235         if(this.initialized){
41236             var v = this.el.dom.value.trim();
41237             
41238 //            if(v.length < 1){
41239 //                v = '&#160;';
41240 //            }
41241             
41242             if(this.owner.fireEvent('beforepush', this, v) !== false){
41243                 var d = (this.doc.body || this.doc.documentElement);
41244                 d.innerHTML = v;
41245                 this.cleanUpPaste();
41246                 this.el.dom.value = d.innerHTML;
41247                 this.owner.fireEvent('push', this, v);
41248             }
41249         }
41250     },
41251
41252     // private
41253     deferFocus : function(){
41254         this.focus.defer(10, this);
41255     },
41256
41257     // doc'ed in Field
41258     focus : function(){
41259         if(this.win && !this.sourceEditMode){
41260             this.win.focus();
41261         }else{
41262             this.el.focus();
41263         }
41264     },
41265     
41266     assignDocWin: function()
41267     {
41268         var iframe = this.iframe;
41269         
41270          if(Roo.isIE){
41271             this.doc = iframe.contentWindow.document;
41272             this.win = iframe.contentWindow;
41273         } else {
41274             if (!Roo.get(this.frameId)) {
41275                 return;
41276             }
41277             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41278             this.win = Roo.get(this.frameId).dom.contentWindow;
41279         }
41280     },
41281     
41282     // private
41283     initEditor : function(){
41284         //console.log("INIT EDITOR");
41285         this.assignDocWin();
41286         
41287         
41288         
41289         this.doc.designMode="on";
41290         this.doc.open();
41291         this.doc.write(this.getDocMarkup());
41292         this.doc.close();
41293         
41294         var dbody = (this.doc.body || this.doc.documentElement);
41295         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41296         // this copies styles from the containing element into thsi one..
41297         // not sure why we need all of this..
41298         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41299         ss['background-attachment'] = 'fixed'; // w3c
41300         dbody.bgProperties = 'fixed'; // ie
41301         Roo.DomHelper.applyStyles(dbody, ss);
41302         Roo.EventManager.on(this.doc, {
41303             //'mousedown': this.onEditorEvent,
41304             'mouseup': this.onEditorEvent,
41305             'dblclick': this.onEditorEvent,
41306             'click': this.onEditorEvent,
41307             'keyup': this.onEditorEvent,
41308             buffer:100,
41309             scope: this
41310         });
41311         if(Roo.isGecko){
41312             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41313         }
41314         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41315             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41316         }
41317         this.initialized = true;
41318
41319         this.owner.fireEvent('initialize', this);
41320         this.pushValue();
41321     },
41322
41323     // private
41324     onDestroy : function(){
41325         
41326         
41327         
41328         if(this.rendered){
41329             
41330             //for (var i =0; i < this.toolbars.length;i++) {
41331             //    // fixme - ask toolbars for heights?
41332             //    this.toolbars[i].onDestroy();
41333            // }
41334             
41335             //this.wrap.dom.innerHTML = '';
41336             //this.wrap.remove();
41337         }
41338     },
41339
41340     // private
41341     onFirstFocus : function(){
41342         
41343         this.assignDocWin();
41344         
41345         
41346         this.activated = true;
41347          
41348     
41349         if(Roo.isGecko){ // prevent silly gecko errors
41350             this.win.focus();
41351             var s = this.win.getSelection();
41352             if(!s.focusNode || s.focusNode.nodeType != 3){
41353                 var r = s.getRangeAt(0);
41354                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41355                 r.collapse(true);
41356                 this.deferFocus();
41357             }
41358             try{
41359                 this.execCmd('useCSS', true);
41360                 this.execCmd('styleWithCSS', false);
41361             }catch(e){}
41362         }
41363         this.owner.fireEvent('activate', this);
41364     },
41365
41366     // private
41367     adjustFont: function(btn){
41368         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41369         //if(Roo.isSafari){ // safari
41370         //    adjust *= 2;
41371        // }
41372         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41373         if(Roo.isSafari){ // safari
41374             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41375             v =  (v < 10) ? 10 : v;
41376             v =  (v > 48) ? 48 : v;
41377             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41378             
41379         }
41380         
41381         
41382         v = Math.max(1, v+adjust);
41383         
41384         this.execCmd('FontSize', v  );
41385     },
41386
41387     onEditorEvent : function(e){
41388         this.owner.fireEvent('editorevent', this, e);
41389       //  this.updateToolbar();
41390         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41391     },
41392
41393     insertTag : function(tg)
41394     {
41395         // could be a bit smarter... -> wrap the current selected tRoo..
41396         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41397             
41398             range = this.createRange(this.getSelection());
41399             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41400             wrappingNode.appendChild(range.extractContents());
41401             range.insertNode(wrappingNode);
41402
41403             return;
41404             
41405             
41406             
41407         }
41408         this.execCmd("formatblock",   tg);
41409         
41410     },
41411     
41412     insertText : function(txt)
41413     {
41414         
41415         
41416         var range = this.createRange();
41417         range.deleteContents();
41418                //alert(Sender.getAttribute('label'));
41419                
41420         range.insertNode(this.doc.createTextNode(txt));
41421     } ,
41422     
41423      
41424
41425     /**
41426      * Executes a Midas editor command on the editor document and performs necessary focus and
41427      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41428      * @param {String} cmd The Midas command
41429      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41430      */
41431     relayCmd : function(cmd, value){
41432         this.win.focus();
41433         this.execCmd(cmd, value);
41434         this.owner.fireEvent('editorevent', this);
41435         //this.updateToolbar();
41436         this.owner.deferFocus();
41437     },
41438
41439     /**
41440      * Executes a Midas editor command directly on the editor document.
41441      * For visual commands, you should use {@link #relayCmd} instead.
41442      * <b>This should only be called after the editor is initialized.</b>
41443      * @param {String} cmd The Midas command
41444      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41445      */
41446     execCmd : function(cmd, value){
41447         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41448         this.syncValue();
41449     },
41450  
41451  
41452    
41453     /**
41454      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41455      * to insert tRoo.
41456      * @param {String} text | dom node.. 
41457      */
41458     insertAtCursor : function(text)
41459     {
41460         
41461         
41462         
41463         if(!this.activated){
41464             return;
41465         }
41466         /*
41467         if(Roo.isIE){
41468             this.win.focus();
41469             var r = this.doc.selection.createRange();
41470             if(r){
41471                 r.collapse(true);
41472                 r.pasteHTML(text);
41473                 this.syncValue();
41474                 this.deferFocus();
41475             
41476             }
41477             return;
41478         }
41479         */
41480         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41481             this.win.focus();
41482             
41483             
41484             // from jquery ui (MIT licenced)
41485             var range, node;
41486             var win = this.win;
41487             
41488             if (win.getSelection && win.getSelection().getRangeAt) {
41489                 range = win.getSelection().getRangeAt(0);
41490                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41491                 range.insertNode(node);
41492             } else if (win.document.selection && win.document.selection.createRange) {
41493                 // no firefox support
41494                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41495                 win.document.selection.createRange().pasteHTML(txt);
41496             } else {
41497                 // no firefox support
41498                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41499                 this.execCmd('InsertHTML', txt);
41500             } 
41501             
41502             this.syncValue();
41503             
41504             this.deferFocus();
41505         }
41506     },
41507  // private
41508     mozKeyPress : function(e){
41509         if(e.ctrlKey){
41510             var c = e.getCharCode(), cmd;
41511           
41512             if(c > 0){
41513                 c = String.fromCharCode(c).toLowerCase();
41514                 switch(c){
41515                     case 'b':
41516                         cmd = 'bold';
41517                         break;
41518                     case 'i':
41519                         cmd = 'italic';
41520                         break;
41521                     
41522                     case 'u':
41523                         cmd = 'underline';
41524                         break;
41525                     
41526                     case 'v':
41527                         this.cleanUpPaste.defer(100, this);
41528                         return;
41529                         
41530                 }
41531                 if(cmd){
41532                     this.win.focus();
41533                     this.execCmd(cmd);
41534                     this.deferFocus();
41535                     e.preventDefault();
41536                 }
41537                 
41538             }
41539         }
41540     },
41541
41542     // private
41543     fixKeys : function(){ // load time branching for fastest keydown performance
41544         if(Roo.isIE){
41545             return function(e){
41546                 var k = e.getKey(), r;
41547                 if(k == e.TAB){
41548                     e.stopEvent();
41549                     r = this.doc.selection.createRange();
41550                     if(r){
41551                         r.collapse(true);
41552                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41553                         this.deferFocus();
41554                     }
41555                     return;
41556                 }
41557                 
41558                 if(k == e.ENTER){
41559                     r = this.doc.selection.createRange();
41560                     if(r){
41561                         var target = r.parentElement();
41562                         if(!target || target.tagName.toLowerCase() != 'li'){
41563                             e.stopEvent();
41564                             r.pasteHTML('<br />');
41565                             r.collapse(false);
41566                             r.select();
41567                         }
41568                     }
41569                 }
41570                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41571                     this.cleanUpPaste.defer(100, this);
41572                     return;
41573                 }
41574                 
41575                 
41576             };
41577         }else if(Roo.isOpera){
41578             return function(e){
41579                 var k = e.getKey();
41580                 if(k == e.TAB){
41581                     e.stopEvent();
41582                     this.win.focus();
41583                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41584                     this.deferFocus();
41585                 }
41586                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41587                     this.cleanUpPaste.defer(100, this);
41588                     return;
41589                 }
41590                 
41591             };
41592         }else if(Roo.isSafari){
41593             return function(e){
41594                 var k = e.getKey();
41595                 
41596                 if(k == e.TAB){
41597                     e.stopEvent();
41598                     this.execCmd('InsertText','\t');
41599                     this.deferFocus();
41600                     return;
41601                 }
41602                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41603                     this.cleanUpPaste.defer(100, this);
41604                     return;
41605                 }
41606                 
41607              };
41608         }
41609     }(),
41610     
41611     getAllAncestors: function()
41612     {
41613         var p = this.getSelectedNode();
41614         var a = [];
41615         if (!p) {
41616             a.push(p); // push blank onto stack..
41617             p = this.getParentElement();
41618         }
41619         
41620         
41621         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41622             a.push(p);
41623             p = p.parentNode;
41624         }
41625         a.push(this.doc.body);
41626         return a;
41627     },
41628     lastSel : false,
41629     lastSelNode : false,
41630     
41631     
41632     getSelection : function() 
41633     {
41634         this.assignDocWin();
41635         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41636     },
41637     
41638     getSelectedNode: function() 
41639     {
41640         // this may only work on Gecko!!!
41641         
41642         // should we cache this!!!!
41643         
41644         
41645         
41646          
41647         var range = this.createRange(this.getSelection()).cloneRange();
41648         
41649         if (Roo.isIE) {
41650             var parent = range.parentElement();
41651             while (true) {
41652                 var testRange = range.duplicate();
41653                 testRange.moveToElementText(parent);
41654                 if (testRange.inRange(range)) {
41655                     break;
41656                 }
41657                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41658                     break;
41659                 }
41660                 parent = parent.parentElement;
41661             }
41662             return parent;
41663         }
41664         
41665         // is ancestor a text element.
41666         var ac =  range.commonAncestorContainer;
41667         if (ac.nodeType == 3) {
41668             ac = ac.parentNode;
41669         }
41670         
41671         var ar = ac.childNodes;
41672          
41673         var nodes = [];
41674         var other_nodes = [];
41675         var has_other_nodes = false;
41676         for (var i=0;i<ar.length;i++) {
41677             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41678                 continue;
41679             }
41680             // fullly contained node.
41681             
41682             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41683                 nodes.push(ar[i]);
41684                 continue;
41685             }
41686             
41687             // probably selected..
41688             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41689                 other_nodes.push(ar[i]);
41690                 continue;
41691             }
41692             // outer..
41693             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41694                 continue;
41695             }
41696             
41697             
41698             has_other_nodes = true;
41699         }
41700         if (!nodes.length && other_nodes.length) {
41701             nodes= other_nodes;
41702         }
41703         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41704             return false;
41705         }
41706         
41707         return nodes[0];
41708     },
41709     createRange: function(sel)
41710     {
41711         // this has strange effects when using with 
41712         // top toolbar - not sure if it's a great idea.
41713         //this.editor.contentWindow.focus();
41714         if (typeof sel != "undefined") {
41715             try {
41716                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41717             } catch(e) {
41718                 return this.doc.createRange();
41719             }
41720         } else {
41721             return this.doc.createRange();
41722         }
41723     },
41724     getParentElement: function()
41725     {
41726         
41727         this.assignDocWin();
41728         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41729         
41730         var range = this.createRange(sel);
41731          
41732         try {
41733             var p = range.commonAncestorContainer;
41734             while (p.nodeType == 3) { // text node
41735                 p = p.parentNode;
41736             }
41737             return p;
41738         } catch (e) {
41739             return null;
41740         }
41741     
41742     },
41743     /***
41744      *
41745      * Range intersection.. the hard stuff...
41746      *  '-1' = before
41747      *  '0' = hits..
41748      *  '1' = after.
41749      *         [ -- selected range --- ]
41750      *   [fail]                        [fail]
41751      *
41752      *    basically..
41753      *      if end is before start or  hits it. fail.
41754      *      if start is after end or hits it fail.
41755      *
41756      *   if either hits (but other is outside. - then it's not 
41757      *   
41758      *    
41759      **/
41760     
41761     
41762     // @see http://www.thismuchiknow.co.uk/?p=64.
41763     rangeIntersectsNode : function(range, node)
41764     {
41765         var nodeRange = node.ownerDocument.createRange();
41766         try {
41767             nodeRange.selectNode(node);
41768         } catch (e) {
41769             nodeRange.selectNodeContents(node);
41770         }
41771     
41772         var rangeStartRange = range.cloneRange();
41773         rangeStartRange.collapse(true);
41774     
41775         var rangeEndRange = range.cloneRange();
41776         rangeEndRange.collapse(false);
41777     
41778         var nodeStartRange = nodeRange.cloneRange();
41779         nodeStartRange.collapse(true);
41780     
41781         var nodeEndRange = nodeRange.cloneRange();
41782         nodeEndRange.collapse(false);
41783     
41784         return rangeStartRange.compareBoundaryPoints(
41785                  Range.START_TO_START, nodeEndRange) == -1 &&
41786                rangeEndRange.compareBoundaryPoints(
41787                  Range.START_TO_START, nodeStartRange) == 1;
41788         
41789          
41790     },
41791     rangeCompareNode : function(range, node)
41792     {
41793         var nodeRange = node.ownerDocument.createRange();
41794         try {
41795             nodeRange.selectNode(node);
41796         } catch (e) {
41797             nodeRange.selectNodeContents(node);
41798         }
41799         
41800         
41801         range.collapse(true);
41802     
41803         nodeRange.collapse(true);
41804      
41805         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41806         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41807          
41808         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41809         
41810         var nodeIsBefore   =  ss == 1;
41811         var nodeIsAfter    = ee == -1;
41812         
41813         if (nodeIsBefore && nodeIsAfter)
41814             return 0; // outer
41815         if (!nodeIsBefore && nodeIsAfter)
41816             return 1; //right trailed.
41817         
41818         if (nodeIsBefore && !nodeIsAfter)
41819             return 2;  // left trailed.
41820         // fully contined.
41821         return 3;
41822     },
41823
41824     // private? - in a new class?
41825     cleanUpPaste :  function()
41826     {
41827         // cleans up the whole document..
41828         Roo.log('cleanuppaste');
41829         
41830         this.cleanUpChildren(this.doc.body);
41831         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41832         if (clean != this.doc.body.innerHTML) {
41833             this.doc.body.innerHTML = clean;
41834         }
41835         
41836     },
41837     
41838     cleanWordChars : function(input) {// change the chars to hex code
41839         var he = Roo.HtmlEditorCore;
41840         
41841         var output = input;
41842         Roo.each(he.swapCodes, function(sw) { 
41843             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41844             
41845             output = output.replace(swapper, sw[1]);
41846         });
41847         
41848         return output;
41849     },
41850     
41851     
41852     cleanUpChildren : function (n)
41853     {
41854         if (!n.childNodes.length) {
41855             return;
41856         }
41857         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41858            this.cleanUpChild(n.childNodes[i]);
41859         }
41860     },
41861     
41862     
41863         
41864     
41865     cleanUpChild : function (node)
41866     {
41867         var ed = this;
41868         //console.log(node);
41869         if (node.nodeName == "#text") {
41870             // clean up silly Windows -- stuff?
41871             return; 
41872         }
41873         if (node.nodeName == "#comment") {
41874             node.parentNode.removeChild(node);
41875             // clean up silly Windows -- stuff?
41876             return; 
41877         }
41878         
41879         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
41880             // remove node.
41881             node.parentNode.removeChild(node);
41882             return;
41883             
41884         }
41885         
41886         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41887         
41888         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41889         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41890         
41891         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41892         //    remove_keep_children = true;
41893         //}
41894         
41895         if (remove_keep_children) {
41896             this.cleanUpChildren(node);
41897             // inserts everything just before this node...
41898             while (node.childNodes.length) {
41899                 var cn = node.childNodes[0];
41900                 node.removeChild(cn);
41901                 node.parentNode.insertBefore(cn, node);
41902             }
41903             node.parentNode.removeChild(node);
41904             return;
41905         }
41906         
41907         if (!node.attributes || !node.attributes.length) {
41908             this.cleanUpChildren(node);
41909             return;
41910         }
41911         
41912         function cleanAttr(n,v)
41913         {
41914             
41915             if (v.match(/^\./) || v.match(/^\//)) {
41916                 return;
41917             }
41918             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41919                 return;
41920             }
41921             if (v.match(/^#/)) {
41922                 return;
41923             }
41924 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41925             node.removeAttribute(n);
41926             
41927         }
41928         
41929         function cleanStyle(n,v)
41930         {
41931             if (v.match(/expression/)) { //XSS?? should we even bother..
41932                 node.removeAttribute(n);
41933                 return;
41934             }
41935             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
41936             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
41937             
41938             
41939             var parts = v.split(/;/);
41940             var clean = [];
41941             
41942             Roo.each(parts, function(p) {
41943                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41944                 if (!p.length) {
41945                     return true;
41946                 }
41947                 var l = p.split(':').shift().replace(/\s+/g,'');
41948                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41949                 
41950                 if ( cblack.indexOf(l) > -1) {
41951 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41952                     //node.removeAttribute(n);
41953                     return true;
41954                 }
41955                 //Roo.log()
41956                 // only allow 'c whitelisted system attributes'
41957                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41958 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41959                     //node.removeAttribute(n);
41960                     return true;
41961                 }
41962                 
41963                 
41964                  
41965                 
41966                 clean.push(p);
41967                 return true;
41968             });
41969             if (clean.length) { 
41970                 node.setAttribute(n, clean.join(';'));
41971             } else {
41972                 node.removeAttribute(n);
41973             }
41974             
41975         }
41976         
41977         
41978         for (var i = node.attributes.length-1; i > -1 ; i--) {
41979             var a = node.attributes[i];
41980             //console.log(a);
41981             
41982             if (a.name.toLowerCase().substr(0,2)=='on')  {
41983                 node.removeAttribute(a.name);
41984                 continue;
41985             }
41986             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
41987                 node.removeAttribute(a.name);
41988                 continue;
41989             }
41990             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
41991                 cleanAttr(a.name,a.value); // fixme..
41992                 continue;
41993             }
41994             if (a.name == 'style') {
41995                 cleanStyle(a.name,a.value);
41996                 continue;
41997             }
41998             /// clean up MS crap..
41999             // tecnically this should be a list of valid class'es..
42000             
42001             
42002             if (a.name == 'class') {
42003                 if (a.value.match(/^Mso/)) {
42004                     node.className = '';
42005                 }
42006                 
42007                 if (a.value.match(/body/)) {
42008                     node.className = '';
42009                 }
42010                 continue;
42011             }
42012             
42013             // style cleanup!?
42014             // class cleanup?
42015             
42016         }
42017         
42018         
42019         this.cleanUpChildren(node);
42020         
42021         
42022     },
42023     /**
42024      * Clean up MS wordisms...
42025      */
42026     cleanWord : function(node)
42027     {
42028         var _t = this;
42029         var cleanWordChildren = function()
42030         {
42031             if (!node.childNodes.length) {
42032                 return;
42033             }
42034             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42035                _t.cleanWord(node.childNodes[i]);
42036             }
42037         }
42038         
42039         
42040         if (!node) {
42041             this.cleanWord(this.doc.body);
42042             return;
42043         }
42044         if (node.nodeName == "#text") {
42045             // clean up silly Windows -- stuff?
42046             return; 
42047         }
42048         if (node.nodeName == "#comment") {
42049             node.parentNode.removeChild(node);
42050             // clean up silly Windows -- stuff?
42051             return; 
42052         }
42053         
42054         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42055             node.parentNode.removeChild(node);
42056             return;
42057         }
42058         
42059         // remove - but keep children..
42060         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42061             while (node.childNodes.length) {
42062                 var cn = node.childNodes[0];
42063                 node.removeChild(cn);
42064                 node.parentNode.insertBefore(cn, node);
42065             }
42066             node.parentNode.removeChild(node);
42067             cleanWordChildren();
42068             return;
42069         }
42070         // clean styles
42071         if (node.className.length) {
42072             
42073             var cn = node.className.split(/\W+/);
42074             var cna = [];
42075             Roo.each(cn, function(cls) {
42076                 if (cls.match(/Mso[a-zA-Z]+/)) {
42077                     return;
42078                 }
42079                 cna.push(cls);
42080             });
42081             node.className = cna.length ? cna.join(' ') : '';
42082             if (!cna.length) {
42083                 node.removeAttribute("class");
42084             }
42085         }
42086         
42087         if (node.hasAttribute("lang")) {
42088             node.removeAttribute("lang");
42089         }
42090         
42091         if (node.hasAttribute("style")) {
42092             
42093             var styles = node.getAttribute("style").split(";");
42094             var nstyle = [];
42095             Roo.each(styles, function(s) {
42096                 if (!s.match(/:/)) {
42097                     return;
42098                 }
42099                 var kv = s.split(":");
42100                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42101                     return;
42102                 }
42103                 // what ever is left... we allow.
42104                 nstyle.push(s);
42105             });
42106             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42107             if (!nstyle.length) {
42108                 node.removeAttribute('style');
42109             }
42110         }
42111         
42112         cleanWordChildren();
42113         
42114         
42115     },
42116     domToHTML : function(currentElement, depth, nopadtext) {
42117         
42118             depth = depth || 0;
42119             nopadtext = nopadtext || false;
42120         
42121             if (!currentElement) {
42122                 return this.domToHTML(this.doc.body);
42123             }
42124             
42125             //Roo.log(currentElement);
42126             var j;
42127             var allText = false;
42128             var nodeName = currentElement.nodeName;
42129             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42130             
42131             if  (nodeName == '#text') {
42132                 return currentElement.nodeValue;
42133             }
42134             
42135             
42136             var ret = '';
42137             if (nodeName != 'BODY') {
42138                  
42139                 var i = 0;
42140                 // Prints the node tagName, such as <A>, <IMG>, etc
42141                 if (tagName) {
42142                     var attr = [];
42143                     for(i = 0; i < currentElement.attributes.length;i++) {
42144                         // quoting?
42145                         var aname = currentElement.attributes.item(i).name;
42146                         if (!currentElement.attributes.item(i).value.length) {
42147                             continue;
42148                         }
42149                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42150                     }
42151                     
42152                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42153                 } 
42154                 else {
42155                     
42156                     // eack
42157                 }
42158             } else {
42159                 tagName = false;
42160             }
42161             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42162                 return ret;
42163             }
42164             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42165                 nopadtext = true;
42166             }
42167             
42168             
42169             // Traverse the tree
42170             i = 0;
42171             var currentElementChild = currentElement.childNodes.item(i);
42172             var allText = true;
42173             var innerHTML  = '';
42174             lastnode = '';
42175             while (currentElementChild) {
42176                 // Formatting code (indent the tree so it looks nice on the screen)
42177                 var nopad = nopadtext;
42178                 if (lastnode == 'SPAN') {
42179                     nopad  = true;
42180                 }
42181                 // text
42182                 if  (currentElementChild.nodeName == '#text') {
42183                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42184                     if (!nopad && toadd.length > 80) {
42185                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42186                     }
42187                     innerHTML  += toadd;
42188                     
42189                     i++;
42190                     currentElementChild = currentElement.childNodes.item(i);
42191                     lastNode = '';
42192                     continue;
42193                 }
42194                 allText = false;
42195                 
42196                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42197                     
42198                 // Recursively traverse the tree structure of the child node
42199                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42200                 lastnode = currentElementChild.nodeName;
42201                 i++;
42202                 currentElementChild=currentElement.childNodes.item(i);
42203             }
42204             
42205             ret += innerHTML;
42206             
42207             if (!allText) {
42208                     // The remaining code is mostly for formatting the tree
42209                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42210             }
42211             
42212             
42213             if (tagName) {
42214                 ret+= "</"+tagName+">";
42215             }
42216             return ret;
42217             
42218         }
42219     
42220     // hide stuff that is not compatible
42221     /**
42222      * @event blur
42223      * @hide
42224      */
42225     /**
42226      * @event change
42227      * @hide
42228      */
42229     /**
42230      * @event focus
42231      * @hide
42232      */
42233     /**
42234      * @event specialkey
42235      * @hide
42236      */
42237     /**
42238      * @cfg {String} fieldClass @hide
42239      */
42240     /**
42241      * @cfg {String} focusClass @hide
42242      */
42243     /**
42244      * @cfg {String} autoCreate @hide
42245      */
42246     /**
42247      * @cfg {String} inputType @hide
42248      */
42249     /**
42250      * @cfg {String} invalidClass @hide
42251      */
42252     /**
42253      * @cfg {String} invalidText @hide
42254      */
42255     /**
42256      * @cfg {String} msgFx @hide
42257      */
42258     /**
42259      * @cfg {String} validateOnBlur @hide
42260      */
42261 });
42262
42263 Roo.HtmlEditorCore.white = [
42264         'area', 'br', 'img', 'input', 'hr', 'wbr',
42265         
42266        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42267        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42268        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42269        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42270        'table',   'ul',         'xmp', 
42271        
42272        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42273       'thead',   'tr', 
42274      
42275       'dir', 'menu', 'ol', 'ul', 'dl',
42276        
42277       'embed',  'object'
42278 ];
42279
42280
42281 Roo.HtmlEditorCore.black = [
42282     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42283         'applet', // 
42284         'base',   'basefont', 'bgsound', 'blink',  'body', 
42285         'frame',  'frameset', 'head',    'html',   'ilayer', 
42286         'iframe', 'layer',  'link',     'meta',    'object',   
42287         'script', 'style' ,'title',  'xml' // clean later..
42288 ];
42289 Roo.HtmlEditorCore.clean = [
42290     'script', 'style', 'title', 'xml'
42291 ];
42292 Roo.HtmlEditorCore.remove = [
42293     'font'
42294 ];
42295 // attributes..
42296
42297 Roo.HtmlEditorCore.ablack = [
42298     'on'
42299 ];
42300     
42301 Roo.HtmlEditorCore.aclean = [ 
42302     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42303 ];
42304
42305 // protocols..
42306 Roo.HtmlEditorCore.pwhite= [
42307         'http',  'https',  'mailto'
42308 ];
42309
42310 // white listed style attributes.
42311 Roo.HtmlEditorCore.cwhite= [
42312       //  'text-align', /// default is to allow most things..
42313       
42314          
42315 //        'font-size'//??
42316 ];
42317
42318 // black listed style attributes.
42319 Roo.HtmlEditorCore.cblack= [
42320       //  'font-size' -- this can be set by the project 
42321 ];
42322
42323
42324 Roo.HtmlEditorCore.swapCodes   =[ 
42325     [    8211, "--" ], 
42326     [    8212, "--" ], 
42327     [    8216,  "'" ],  
42328     [    8217, "'" ],  
42329     [    8220, '"' ],  
42330     [    8221, '"' ],  
42331     [    8226, "*" ],  
42332     [    8230, "..." ]
42333 ]; 
42334
42335     //<script type="text/javascript">
42336
42337 /*
42338  * Ext JS Library 1.1.1
42339  * Copyright(c) 2006-2007, Ext JS, LLC.
42340  * Licence LGPL
42341  * 
42342  */
42343  
42344  
42345 Roo.form.HtmlEditor = function(config){
42346     
42347     
42348     
42349     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42350     
42351     if (!this.toolbars) {
42352         this.toolbars = [];
42353     }
42354     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42355     
42356     
42357 };
42358
42359 /**
42360  * @class Roo.form.HtmlEditor
42361  * @extends Roo.form.Field
42362  * Provides a lightweight HTML Editor component.
42363  *
42364  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42365  * 
42366  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42367  * supported by this editor.</b><br/><br/>
42368  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42369  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42370  */
42371 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42372     /**
42373      * @cfg {Boolean} clearUp
42374      */
42375     clearUp : true,
42376       /**
42377      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42378      */
42379     toolbars : false,
42380    
42381      /**
42382      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42383      *                        Roo.resizable.
42384      */
42385     resizable : false,
42386      /**
42387      * @cfg {Number} height (in pixels)
42388      */   
42389     height: 300,
42390    /**
42391      * @cfg {Number} width (in pixels)
42392      */   
42393     width: 500,
42394     
42395     /**
42396      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42397      * 
42398      */
42399     stylesheets: false,
42400     
42401     // id of frame..
42402     frameId: false,
42403     
42404     // private properties
42405     validationEvent : false,
42406     deferHeight: true,
42407     initialized : false,
42408     activated : false,
42409     
42410     onFocus : Roo.emptyFn,
42411     iframePad:3,
42412     hideMode:'offsets',
42413     
42414     defaultAutoCreate : { // modified by initCompnoent..
42415         tag: "textarea",
42416         style:"width:500px;height:300px;",
42417         autocomplete: "off"
42418     },
42419
42420     // private
42421     initComponent : function(){
42422         this.addEvents({
42423             /**
42424              * @event initialize
42425              * Fires when the editor is fully initialized (including the iframe)
42426              * @param {HtmlEditor} this
42427              */
42428             initialize: true,
42429             /**
42430              * @event activate
42431              * Fires when the editor is first receives the focus. Any insertion must wait
42432              * until after this event.
42433              * @param {HtmlEditor} this
42434              */
42435             activate: true,
42436              /**
42437              * @event beforesync
42438              * Fires before the textarea is updated with content from the editor iframe. Return false
42439              * to cancel the sync.
42440              * @param {HtmlEditor} this
42441              * @param {String} html
42442              */
42443             beforesync: true,
42444              /**
42445              * @event beforepush
42446              * Fires before the iframe editor is updated with content from the textarea. Return false
42447              * to cancel the push.
42448              * @param {HtmlEditor} this
42449              * @param {String} html
42450              */
42451             beforepush: true,
42452              /**
42453              * @event sync
42454              * Fires when the textarea is updated with content from the editor iframe.
42455              * @param {HtmlEditor} this
42456              * @param {String} html
42457              */
42458             sync: true,
42459              /**
42460              * @event push
42461              * Fires when the iframe editor is updated with content from the textarea.
42462              * @param {HtmlEditor} this
42463              * @param {String} html
42464              */
42465             push: true,
42466              /**
42467              * @event editmodechange
42468              * Fires when the editor switches edit modes
42469              * @param {HtmlEditor} this
42470              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42471              */
42472             editmodechange: true,
42473             /**
42474              * @event editorevent
42475              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42476              * @param {HtmlEditor} this
42477              */
42478             editorevent: true,
42479             /**
42480              * @event firstfocus
42481              * Fires when on first focus - needed by toolbars..
42482              * @param {HtmlEditor} this
42483              */
42484             firstfocus: true,
42485             /**
42486              * @event autosave
42487              * Auto save the htmlEditor value as a file into Events
42488              * @param {HtmlEditor} this
42489              */
42490             autosave: true,
42491             /**
42492              * @event savedpreview
42493              * preview the saved version of htmlEditor
42494              * @param {HtmlEditor} this
42495              */
42496             savedpreview: true
42497         });
42498         this.defaultAutoCreate =  {
42499             tag: "textarea",
42500             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42501             autocomplete: "off"
42502         };
42503     },
42504
42505     /**
42506      * Protected method that will not generally be called directly. It
42507      * is called when the editor creates its toolbar. Override this method if you need to
42508      * add custom toolbar buttons.
42509      * @param {HtmlEditor} editor
42510      */
42511     createToolbar : function(editor){
42512         Roo.log("create toolbars");
42513         if (!editor.toolbars || !editor.toolbars.length) {
42514             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42515         }
42516         
42517         for (var i =0 ; i < editor.toolbars.length;i++) {
42518             editor.toolbars[i] = Roo.factory(
42519                     typeof(editor.toolbars[i]) == 'string' ?
42520                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42521                 Roo.form.HtmlEditor);
42522             editor.toolbars[i].init(editor);
42523         }
42524          
42525         
42526     },
42527
42528      
42529     // private
42530     onRender : function(ct, position)
42531     {
42532         var _t = this;
42533         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42534         
42535         this.wrap = this.el.wrap({
42536             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42537         });
42538         
42539         this.editorcore.onRender(ct, position);
42540          
42541         if (this.resizable) {
42542             this.resizeEl = new Roo.Resizable(this.wrap, {
42543                 pinned : true,
42544                 wrap: true,
42545                 dynamic : true,
42546                 minHeight : this.height,
42547                 height: this.height,
42548                 handles : this.resizable,
42549                 width: this.width,
42550                 listeners : {
42551                     resize : function(r, w, h) {
42552                         _t.onResize(w,h); // -something
42553                     }
42554                 }
42555             });
42556             
42557         }
42558         this.createToolbar(this);
42559        
42560         
42561         if(!this.width){
42562             this.setSize(this.wrap.getSize());
42563         }
42564         if (this.resizeEl) {
42565             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42566             // should trigger onReize..
42567         }
42568         
42569 //        if(this.autosave && this.w){
42570 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42571 //        }
42572     },
42573
42574     // private
42575     onResize : function(w, h)
42576     {
42577         //Roo.log('resize: ' +w + ',' + h );
42578         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42579         var ew = false;
42580         var eh = false;
42581         
42582         if(this.el ){
42583             if(typeof w == 'number'){
42584                 var aw = w - this.wrap.getFrameWidth('lr');
42585                 this.el.setWidth(this.adjustWidth('textarea', aw));
42586                 ew = aw;
42587             }
42588             if(typeof h == 'number'){
42589                 var tbh = 0;
42590                 for (var i =0; i < this.toolbars.length;i++) {
42591                     // fixme - ask toolbars for heights?
42592                     tbh += this.toolbars[i].tb.el.getHeight();
42593                     if (this.toolbars[i].footer) {
42594                         tbh += this.toolbars[i].footer.el.getHeight();
42595                     }
42596                 }
42597                 
42598                 
42599                 
42600                 
42601                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42602                 ah -= 5; // knock a few pixes off for look..
42603                 this.el.setHeight(this.adjustWidth('textarea', ah));
42604                 var eh = ah;
42605             }
42606         }
42607         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42608         this.editorcore.onResize(ew,eh);
42609         
42610     },
42611
42612     /**
42613      * Toggles the editor between standard and source edit mode.
42614      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42615      */
42616     toggleSourceEdit : function(sourceEditMode)
42617     {
42618         this.editorcore.toggleSourceEdit(sourceEditMode);
42619         
42620         if(this.editorcore.sourceEditMode){
42621             Roo.log('editor - showing textarea');
42622             
42623 //            Roo.log('in');
42624 //            Roo.log(this.syncValue());
42625             this.editorcore.syncValue();
42626             this.el.removeClass('x-hidden');
42627             this.el.dom.removeAttribute('tabIndex');
42628             this.el.focus();
42629         }else{
42630             Roo.log('editor - hiding textarea');
42631 //            Roo.log('out')
42632 //            Roo.log(this.pushValue()); 
42633             this.editorcore.pushValue();
42634             
42635             this.el.addClass('x-hidden');
42636             this.el.dom.setAttribute('tabIndex', -1);
42637             //this.deferFocus();
42638         }
42639          
42640         this.setSize(this.wrap.getSize());
42641         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42642     },
42643  
42644     // private (for BoxComponent)
42645     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42646
42647     // private (for BoxComponent)
42648     getResizeEl : function(){
42649         return this.wrap;
42650     },
42651
42652     // private (for BoxComponent)
42653     getPositionEl : function(){
42654         return this.wrap;
42655     },
42656
42657     // private
42658     initEvents : function(){
42659         this.originalValue = this.getValue();
42660     },
42661
42662     /**
42663      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42664      * @method
42665      */
42666     markInvalid : Roo.emptyFn,
42667     /**
42668      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42669      * @method
42670      */
42671     clearInvalid : Roo.emptyFn,
42672
42673     setValue : function(v){
42674         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42675         this.editorcore.pushValue();
42676     },
42677
42678      
42679     // private
42680     deferFocus : function(){
42681         this.focus.defer(10, this);
42682     },
42683
42684     // doc'ed in Field
42685     focus : function(){
42686         this.editorcore.focus();
42687         
42688     },
42689       
42690
42691     // private
42692     onDestroy : function(){
42693         
42694         
42695         
42696         if(this.rendered){
42697             
42698             for (var i =0; i < this.toolbars.length;i++) {
42699                 // fixme - ask toolbars for heights?
42700                 this.toolbars[i].onDestroy();
42701             }
42702             
42703             this.wrap.dom.innerHTML = '';
42704             this.wrap.remove();
42705         }
42706     },
42707
42708     // private
42709     onFirstFocus : function(){
42710         //Roo.log("onFirstFocus");
42711         this.editorcore.onFirstFocus();
42712          for (var i =0; i < this.toolbars.length;i++) {
42713             this.toolbars[i].onFirstFocus();
42714         }
42715         
42716     },
42717     
42718     // private
42719     syncValue : function()
42720     {
42721         this.editorcore.syncValue();
42722     },
42723     
42724     pushValue : function()
42725     {
42726         this.editorcore.pushValue();
42727     }
42728      
42729     
42730     // hide stuff that is not compatible
42731     /**
42732      * @event blur
42733      * @hide
42734      */
42735     /**
42736      * @event change
42737      * @hide
42738      */
42739     /**
42740      * @event focus
42741      * @hide
42742      */
42743     /**
42744      * @event specialkey
42745      * @hide
42746      */
42747     /**
42748      * @cfg {String} fieldClass @hide
42749      */
42750     /**
42751      * @cfg {String} focusClass @hide
42752      */
42753     /**
42754      * @cfg {String} autoCreate @hide
42755      */
42756     /**
42757      * @cfg {String} inputType @hide
42758      */
42759     /**
42760      * @cfg {String} invalidClass @hide
42761      */
42762     /**
42763      * @cfg {String} invalidText @hide
42764      */
42765     /**
42766      * @cfg {String} msgFx @hide
42767      */
42768     /**
42769      * @cfg {String} validateOnBlur @hide
42770      */
42771 });
42772  
42773     // <script type="text/javascript">
42774 /*
42775  * Based on
42776  * Ext JS Library 1.1.1
42777  * Copyright(c) 2006-2007, Ext JS, LLC.
42778  *  
42779  
42780  */
42781
42782 /**
42783  * @class Roo.form.HtmlEditorToolbar1
42784  * Basic Toolbar
42785  * 
42786  * Usage:
42787  *
42788  new Roo.form.HtmlEditor({
42789     ....
42790     toolbars : [
42791         new Roo.form.HtmlEditorToolbar1({
42792             disable : { fonts: 1 , format: 1, ..., ... , ...],
42793             btns : [ .... ]
42794         })
42795     }
42796      
42797  * 
42798  * @cfg {Object} disable List of elements to disable..
42799  * @cfg {Array} btns List of additional buttons.
42800  * 
42801  * 
42802  * NEEDS Extra CSS? 
42803  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42804  */
42805  
42806 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42807 {
42808     
42809     Roo.apply(this, config);
42810     
42811     // default disabled, based on 'good practice'..
42812     this.disable = this.disable || {};
42813     Roo.applyIf(this.disable, {
42814         fontSize : true,
42815         colors : true,
42816         specialElements : true
42817     });
42818     
42819     
42820     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42821     // dont call parent... till later.
42822 }
42823
42824 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42825     
42826     tb: false,
42827     
42828     rendered: false,
42829     
42830     editor : false,
42831     editorcore : false,
42832     /**
42833      * @cfg {Object} disable  List of toolbar elements to disable
42834          
42835      */
42836     disable : false,
42837     
42838     
42839      /**
42840      * @cfg {String} createLinkText The default text for the create link prompt
42841      */
42842     createLinkText : 'Please enter the URL for the link:',
42843     /**
42844      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42845      */
42846     defaultLinkValue : 'http:/'+'/',
42847    
42848     
42849       /**
42850      * @cfg {Array} fontFamilies An array of available font families
42851      */
42852     fontFamilies : [
42853         'Arial',
42854         'Courier New',
42855         'Tahoma',
42856         'Times New Roman',
42857         'Verdana'
42858     ],
42859     
42860     specialChars : [
42861            "&#169;",
42862           "&#174;",     
42863           "&#8482;",    
42864           "&#163;" ,    
42865          // "&#8212;",    
42866           "&#8230;",    
42867           "&#247;" ,    
42868         //  "&#225;" ,     ?? a acute?
42869            "&#8364;"    , //Euro
42870        //   "&#8220;"    ,
42871         //  "&#8221;"    ,
42872         //  "&#8226;"    ,
42873           "&#176;"  //   , // degrees
42874
42875          // "&#233;"     , // e ecute
42876          // "&#250;"     , // u ecute?
42877     ],
42878     
42879     specialElements : [
42880         {
42881             text: "Insert Table",
42882             xtype: 'MenuItem',
42883             xns : Roo.Menu,
42884             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42885                 
42886         },
42887         {    
42888             text: "Insert Image",
42889             xtype: 'MenuItem',
42890             xns : Roo.Menu,
42891             ihtml : '<img src="about:blank"/>'
42892             
42893         }
42894         
42895          
42896     ],
42897     
42898     
42899     inputElements : [ 
42900             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42901             "input:submit", "input:button", "select", "textarea", "label" ],
42902     formats : [
42903         ["p"] ,  
42904         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42905         ["pre"],[ "code"], 
42906         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42907         ['div'],['span']
42908     ],
42909     
42910     cleanStyles : [
42911         "font-size"
42912     ],
42913      /**
42914      * @cfg {String} defaultFont default font to use.
42915      */
42916     defaultFont: 'tahoma',
42917    
42918     fontSelect : false,
42919     
42920     
42921     formatCombo : false,
42922     
42923     init : function(editor)
42924     {
42925         this.editor = editor;
42926         this.editorcore = editor.editorcore ? editor.editorcore : editor;
42927         var editorcore = this.editorcore;
42928         
42929         var _t = this;
42930         
42931         var fid = editorcore.frameId;
42932         var etb = this;
42933         function btn(id, toggle, handler){
42934             var xid = fid + '-'+ id ;
42935             return {
42936                 id : xid,
42937                 cmd : id,
42938                 cls : 'x-btn-icon x-edit-'+id,
42939                 enableToggle:toggle !== false,
42940                 scope: _t, // was editor...
42941                 handler:handler||_t.relayBtnCmd,
42942                 clickEvent:'mousedown',
42943                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42944                 tabIndex:-1
42945             };
42946         }
42947         
42948         
42949         
42950         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42951         this.tb = tb;
42952          // stop form submits
42953         tb.el.on('click', function(e){
42954             e.preventDefault(); // what does this do?
42955         });
42956
42957         if(!this.disable.font) { // && !Roo.isSafari){
42958             /* why no safari for fonts 
42959             editor.fontSelect = tb.el.createChild({
42960                 tag:'select',
42961                 tabIndex: -1,
42962                 cls:'x-font-select',
42963                 html: this.createFontOptions()
42964             });
42965             
42966             editor.fontSelect.on('change', function(){
42967                 var font = editor.fontSelect.dom.value;
42968                 editor.relayCmd('fontname', font);
42969                 editor.deferFocus();
42970             }, editor);
42971             
42972             tb.add(
42973                 editor.fontSelect.dom,
42974                 '-'
42975             );
42976             */
42977             
42978         };
42979         if(!this.disable.formats){
42980             this.formatCombo = new Roo.form.ComboBox({
42981                 store: new Roo.data.SimpleStore({
42982                     id : 'tag',
42983                     fields: ['tag'],
42984                     data : this.formats // from states.js
42985                 }),
42986                 blockFocus : true,
42987                 name : '',
42988                 //autoCreate : {tag: "div",  size: "20"},
42989                 displayField:'tag',
42990                 typeAhead: false,
42991                 mode: 'local',
42992                 editable : false,
42993                 triggerAction: 'all',
42994                 emptyText:'Add tag',
42995                 selectOnFocus:true,
42996                 width:135,
42997                 listeners : {
42998                     'select': function(c, r, i) {
42999                         editorcore.insertTag(r.get('tag'));
43000                         editor.focus();
43001                     }
43002                 }
43003
43004             });
43005             tb.addField(this.formatCombo);
43006             
43007         }
43008         
43009         if(!this.disable.format){
43010             tb.add(
43011                 btn('bold'),
43012                 btn('italic'),
43013                 btn('underline')
43014             );
43015         };
43016         if(!this.disable.fontSize){
43017             tb.add(
43018                 '-',
43019                 
43020                 
43021                 btn('increasefontsize', false, editorcore.adjustFont),
43022                 btn('decreasefontsize', false, editorcore.adjustFont)
43023             );
43024         };
43025         
43026         
43027         if(!this.disable.colors){
43028             tb.add(
43029                 '-', {
43030                     id:editorcore.frameId +'-forecolor',
43031                     cls:'x-btn-icon x-edit-forecolor',
43032                     clickEvent:'mousedown',
43033                     tooltip: this.buttonTips['forecolor'] || undefined,
43034                     tabIndex:-1,
43035                     menu : new Roo.menu.ColorMenu({
43036                         allowReselect: true,
43037                         focus: Roo.emptyFn,
43038                         value:'000000',
43039                         plain:true,
43040                         selectHandler: function(cp, color){
43041                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43042                             editor.deferFocus();
43043                         },
43044                         scope: editorcore,
43045                         clickEvent:'mousedown'
43046                     })
43047                 }, {
43048                     id:editorcore.frameId +'backcolor',
43049                     cls:'x-btn-icon x-edit-backcolor',
43050                     clickEvent:'mousedown',
43051                     tooltip: this.buttonTips['backcolor'] || undefined,
43052                     tabIndex:-1,
43053                     menu : new Roo.menu.ColorMenu({
43054                         focus: Roo.emptyFn,
43055                         value:'FFFFFF',
43056                         plain:true,
43057                         allowReselect: true,
43058                         selectHandler: function(cp, color){
43059                             if(Roo.isGecko){
43060                                 editorcore.execCmd('useCSS', false);
43061                                 editorcore.execCmd('hilitecolor', color);
43062                                 editorcore.execCmd('useCSS', true);
43063                                 editor.deferFocus();
43064                             }else{
43065                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43066                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43067                                 editor.deferFocus();
43068                             }
43069                         },
43070                         scope:editorcore,
43071                         clickEvent:'mousedown'
43072                     })
43073                 }
43074             );
43075         };
43076         // now add all the items...
43077         
43078
43079         if(!this.disable.alignments){
43080             tb.add(
43081                 '-',
43082                 btn('justifyleft'),
43083                 btn('justifycenter'),
43084                 btn('justifyright')
43085             );
43086         };
43087
43088         //if(!Roo.isSafari){
43089             if(!this.disable.links){
43090                 tb.add(
43091                     '-',
43092                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43093                 );
43094             };
43095
43096             if(!this.disable.lists){
43097                 tb.add(
43098                     '-',
43099                     btn('insertorderedlist'),
43100                     btn('insertunorderedlist')
43101                 );
43102             }
43103             if(!this.disable.sourceEdit){
43104                 tb.add(
43105                     '-',
43106                     btn('sourceedit', true, function(btn){
43107                         Roo.log(this);
43108                         this.toggleSourceEdit(btn.pressed);
43109                     })
43110                 );
43111             }
43112         //}
43113         
43114         var smenu = { };
43115         // special menu.. - needs to be tidied up..
43116         if (!this.disable.special) {
43117             smenu = {
43118                 text: "&#169;",
43119                 cls: 'x-edit-none',
43120                 
43121                 menu : {
43122                     items : []
43123                 }
43124             };
43125             for (var i =0; i < this.specialChars.length; i++) {
43126                 smenu.menu.items.push({
43127                     
43128                     html: this.specialChars[i],
43129                     handler: function(a,b) {
43130                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43131                         //editor.insertAtCursor(a.html);
43132                         
43133                     },
43134                     tabIndex:-1
43135                 });
43136             }
43137             
43138             
43139             tb.add(smenu);
43140             
43141             
43142         }
43143         
43144         var cmenu = { };
43145         if (!this.disable.cleanStyles) {
43146             cmenu = {
43147                 cls: 'x-btn-icon x-btn-clear',
43148                 
43149                 menu : {
43150                     items : []
43151                 }
43152             };
43153             for (var i =0; i < this.cleanStyles.length; i++) {
43154                 cmenu.menu.items.push({
43155                     actiontype : this.cleanStyles[i],
43156                     html: 'Remove ' + this.cleanStyles[i],
43157                     handler: function(a,b) {
43158                         Roo.log(a);
43159                         Roo.log(b);
43160                         var c = Roo.get(editorcore.doc.body);
43161                         c.select('[style]').each(function(s) {
43162                             s.dom.style.removeProperty(a.actiontype);
43163                         });
43164                         editorcore.syncValue();
43165                     },
43166                     tabIndex:-1
43167                 });
43168             }
43169             cmenu.menu.items.push({
43170                 actiontype : 'word',
43171                 html: 'Remove MS Word Formating',
43172                 handler: function(a,b) {
43173                     editorcore.cleanWord();
43174                     editorcore.syncValue();
43175                 },
43176                 tabIndex:-1
43177             });
43178             
43179             cmenu.menu.items.push({
43180                 actiontype : 'all',
43181                 html: 'Remove All Styles',
43182                 handler: function(a,b) {
43183                     
43184                     var c = Roo.get(editorcore.doc.body);
43185                     c.select('[style]').each(function(s) {
43186                         s.dom.removeAttribute('style');
43187                     });
43188                     editorcore.syncValue();
43189                 },
43190                 tabIndex:-1
43191             });
43192              cmenu.menu.items.push({
43193                 actiontype : 'word',
43194                 html: 'Tidy HTML Source',
43195                 handler: function(a,b) {
43196                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43197                     editorcore.syncValue();
43198                 },
43199                 tabIndex:-1
43200             });
43201             
43202             
43203             tb.add(cmenu);
43204         }
43205          
43206         if (!this.disable.specialElements) {
43207             var semenu = {
43208                 text: "Other;",
43209                 cls: 'x-edit-none',
43210                 menu : {
43211                     items : []
43212                 }
43213             };
43214             for (var i =0; i < this.specialElements.length; i++) {
43215                 semenu.menu.items.push(
43216                     Roo.apply({ 
43217                         handler: function(a,b) {
43218                             editor.insertAtCursor(this.ihtml);
43219                         }
43220                     }, this.specialElements[i])
43221                 );
43222                     
43223             }
43224             
43225             tb.add(semenu);
43226             
43227             
43228         }
43229          
43230         
43231         if (this.btns) {
43232             for(var i =0; i< this.btns.length;i++) {
43233                 var b = Roo.factory(this.btns[i],Roo.form);
43234                 b.cls =  'x-edit-none';
43235                 b.scope = editorcore;
43236                 tb.add(b);
43237             }
43238         
43239         }
43240         
43241         
43242         
43243         // disable everything...
43244         
43245         this.tb.items.each(function(item){
43246            if(item.id != editorcore.frameId+ '-sourceedit'){
43247                 item.disable();
43248             }
43249         });
43250         this.rendered = true;
43251         
43252         // the all the btns;
43253         editor.on('editorevent', this.updateToolbar, this);
43254         // other toolbars need to implement this..
43255         //editor.on('editmodechange', this.updateToolbar, this);
43256     },
43257     
43258     
43259     relayBtnCmd : function(btn) {
43260         this.editorcore.relayCmd(btn.cmd);
43261     },
43262     // private used internally
43263     createLink : function(){
43264         Roo.log("create link?");
43265         var url = prompt(this.createLinkText, this.defaultLinkValue);
43266         if(url && url != 'http:/'+'/'){
43267             this.editorcore.relayCmd('createlink', url);
43268         }
43269     },
43270
43271     
43272     /**
43273      * Protected method that will not generally be called directly. It triggers
43274      * a toolbar update by reading the markup state of the current selection in the editor.
43275      */
43276     updateToolbar: function(){
43277
43278         if(!this.editorcore.activated){
43279             this.editor.onFirstFocus();
43280             return;
43281         }
43282
43283         var btns = this.tb.items.map, 
43284             doc = this.editorcore.doc,
43285             frameId = this.editorcore.frameId;
43286
43287         if(!this.disable.font && !Roo.isSafari){
43288             /*
43289             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43290             if(name != this.fontSelect.dom.value){
43291                 this.fontSelect.dom.value = name;
43292             }
43293             */
43294         }
43295         if(!this.disable.format){
43296             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43297             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43298             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43299         }
43300         if(!this.disable.alignments){
43301             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43302             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43303             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43304         }
43305         if(!Roo.isSafari && !this.disable.lists){
43306             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43307             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43308         }
43309         
43310         var ans = this.editorcore.getAllAncestors();
43311         if (this.formatCombo) {
43312             
43313             
43314             var store = this.formatCombo.store;
43315             this.formatCombo.setValue("");
43316             for (var i =0; i < ans.length;i++) {
43317                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43318                     // select it..
43319                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43320                     break;
43321                 }
43322             }
43323         }
43324         
43325         
43326         
43327         // hides menus... - so this cant be on a menu...
43328         Roo.menu.MenuMgr.hideAll();
43329
43330         //this.editorsyncValue();
43331     },
43332    
43333     
43334     createFontOptions : function(){
43335         var buf = [], fs = this.fontFamilies, ff, lc;
43336         
43337         
43338         
43339         for(var i = 0, len = fs.length; i< len; i++){
43340             ff = fs[i];
43341             lc = ff.toLowerCase();
43342             buf.push(
43343                 '<option value="',lc,'" style="font-family:',ff,';"',
43344                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43345                     ff,
43346                 '</option>'
43347             );
43348         }
43349         return buf.join('');
43350     },
43351     
43352     toggleSourceEdit : function(sourceEditMode){
43353         
43354         Roo.log("toolbar toogle");
43355         if(sourceEditMode === undefined){
43356             sourceEditMode = !this.sourceEditMode;
43357         }
43358         this.sourceEditMode = sourceEditMode === true;
43359         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43360         // just toggle the button?
43361         if(btn.pressed !== this.sourceEditMode){
43362             btn.toggle(this.sourceEditMode);
43363             return;
43364         }
43365         
43366         if(sourceEditMode){
43367             Roo.log("disabling buttons");
43368             this.tb.items.each(function(item){
43369                 if(item.cmd != 'sourceedit'){
43370                     item.disable();
43371                 }
43372             });
43373           
43374         }else{
43375             Roo.log("enabling buttons");
43376             if(this.editorcore.initialized){
43377                 this.tb.items.each(function(item){
43378                     item.enable();
43379                 });
43380             }
43381             
43382         }
43383         Roo.log("calling toggole on editor");
43384         // tell the editor that it's been pressed..
43385         this.editor.toggleSourceEdit(sourceEditMode);
43386        
43387     },
43388      /**
43389      * Object collection of toolbar tooltips for the buttons in the editor. The key
43390      * is the command id associated with that button and the value is a valid QuickTips object.
43391      * For example:
43392 <pre><code>
43393 {
43394     bold : {
43395         title: 'Bold (Ctrl+B)',
43396         text: 'Make the selected text bold.',
43397         cls: 'x-html-editor-tip'
43398     },
43399     italic : {
43400         title: 'Italic (Ctrl+I)',
43401         text: 'Make the selected text italic.',
43402         cls: 'x-html-editor-tip'
43403     },
43404     ...
43405 </code></pre>
43406     * @type Object
43407      */
43408     buttonTips : {
43409         bold : {
43410             title: 'Bold (Ctrl+B)',
43411             text: 'Make the selected text bold.',
43412             cls: 'x-html-editor-tip'
43413         },
43414         italic : {
43415             title: 'Italic (Ctrl+I)',
43416             text: 'Make the selected text italic.',
43417             cls: 'x-html-editor-tip'
43418         },
43419         underline : {
43420             title: 'Underline (Ctrl+U)',
43421             text: 'Underline the selected text.',
43422             cls: 'x-html-editor-tip'
43423         },
43424         increasefontsize : {
43425             title: 'Grow Text',
43426             text: 'Increase the font size.',
43427             cls: 'x-html-editor-tip'
43428         },
43429         decreasefontsize : {
43430             title: 'Shrink Text',
43431             text: 'Decrease the font size.',
43432             cls: 'x-html-editor-tip'
43433         },
43434         backcolor : {
43435             title: 'Text Highlight Color',
43436             text: 'Change the background color of the selected text.',
43437             cls: 'x-html-editor-tip'
43438         },
43439         forecolor : {
43440             title: 'Font Color',
43441             text: 'Change the color of the selected text.',
43442             cls: 'x-html-editor-tip'
43443         },
43444         justifyleft : {
43445             title: 'Align Text Left',
43446             text: 'Align text to the left.',
43447             cls: 'x-html-editor-tip'
43448         },
43449         justifycenter : {
43450             title: 'Center Text',
43451             text: 'Center text in the editor.',
43452             cls: 'x-html-editor-tip'
43453         },
43454         justifyright : {
43455             title: 'Align Text Right',
43456             text: 'Align text to the right.',
43457             cls: 'x-html-editor-tip'
43458         },
43459         insertunorderedlist : {
43460             title: 'Bullet List',
43461             text: 'Start a bulleted list.',
43462             cls: 'x-html-editor-tip'
43463         },
43464         insertorderedlist : {
43465             title: 'Numbered List',
43466             text: 'Start a numbered list.',
43467             cls: 'x-html-editor-tip'
43468         },
43469         createlink : {
43470             title: 'Hyperlink',
43471             text: 'Make the selected text a hyperlink.',
43472             cls: 'x-html-editor-tip'
43473         },
43474         sourceedit : {
43475             title: 'Source Edit',
43476             text: 'Switch to source editing mode.',
43477             cls: 'x-html-editor-tip'
43478         }
43479     },
43480     // private
43481     onDestroy : function(){
43482         if(this.rendered){
43483             
43484             this.tb.items.each(function(item){
43485                 if(item.menu){
43486                     item.menu.removeAll();
43487                     if(item.menu.el){
43488                         item.menu.el.destroy();
43489                     }
43490                 }
43491                 item.destroy();
43492             });
43493              
43494         }
43495     },
43496     onFirstFocus: function() {
43497         this.tb.items.each(function(item){
43498            item.enable();
43499         });
43500     }
43501 });
43502
43503
43504
43505
43506 // <script type="text/javascript">
43507 /*
43508  * Based on
43509  * Ext JS Library 1.1.1
43510  * Copyright(c) 2006-2007, Ext JS, LLC.
43511  *  
43512  
43513  */
43514
43515  
43516 /**
43517  * @class Roo.form.HtmlEditor.ToolbarContext
43518  * Context Toolbar
43519  * 
43520  * Usage:
43521  *
43522  new Roo.form.HtmlEditor({
43523     ....
43524     toolbars : [
43525         { xtype: 'ToolbarStandard', styles : {} }
43526         { xtype: 'ToolbarContext', disable : {} }
43527     ]
43528 })
43529
43530      
43531  * 
43532  * @config : {Object} disable List of elements to disable.. (not done yet.)
43533  * @config : {Object} styles  Map of styles available.
43534  * 
43535  */
43536
43537 Roo.form.HtmlEditor.ToolbarContext = function(config)
43538 {
43539     
43540     Roo.apply(this, config);
43541     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43542     // dont call parent... till later.
43543     this.styles = this.styles || {};
43544 }
43545
43546  
43547
43548 Roo.form.HtmlEditor.ToolbarContext.types = {
43549     'IMG' : {
43550         width : {
43551             title: "Width",
43552             width: 40
43553         },
43554         height:  {
43555             title: "Height",
43556             width: 40
43557         },
43558         align: {
43559             title: "Align",
43560             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43561             width : 80
43562             
43563         },
43564         border: {
43565             title: "Border",
43566             width: 40
43567         },
43568         alt: {
43569             title: "Alt",
43570             width: 120
43571         },
43572         src : {
43573             title: "Src",
43574             width: 220
43575         }
43576         
43577     },
43578     'A' : {
43579         name : {
43580             title: "Name",
43581             width: 50
43582         },
43583         target:  {
43584             title: "Target",
43585             width: 120
43586         },
43587         href:  {
43588             title: "Href",
43589             width: 220
43590         } // border?
43591         
43592     },
43593     'TABLE' : {
43594         rows : {
43595             title: "Rows",
43596             width: 20
43597         },
43598         cols : {
43599             title: "Cols",
43600             width: 20
43601         },
43602         width : {
43603             title: "Width",
43604             width: 40
43605         },
43606         height : {
43607             title: "Height",
43608             width: 40
43609         },
43610         border : {
43611             title: "Border",
43612             width: 20
43613         }
43614     },
43615     'TD' : {
43616         width : {
43617             title: "Width",
43618             width: 40
43619         },
43620         height : {
43621             title: "Height",
43622             width: 40
43623         },   
43624         align: {
43625             title: "Align",
43626             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43627             width: 80
43628         },
43629         valign: {
43630             title: "Valign",
43631             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43632             width: 80
43633         },
43634         colspan: {
43635             title: "Colspan",
43636             width: 20
43637             
43638         },
43639          'font-family'  : {
43640             title : "Font",
43641             style : 'fontFamily',
43642             displayField: 'display',
43643             optname : 'font-family',
43644             width: 140
43645         }
43646     },
43647     'INPUT' : {
43648         name : {
43649             title: "name",
43650             width: 120
43651         },
43652         value : {
43653             title: "Value",
43654             width: 120
43655         },
43656         width : {
43657             title: "Width",
43658             width: 40
43659         }
43660     },
43661     'LABEL' : {
43662         'for' : {
43663             title: "For",
43664             width: 120
43665         }
43666     },
43667     'TEXTAREA' : {
43668           name : {
43669             title: "name",
43670             width: 120
43671         },
43672         rows : {
43673             title: "Rows",
43674             width: 20
43675         },
43676         cols : {
43677             title: "Cols",
43678             width: 20
43679         }
43680     },
43681     'SELECT' : {
43682         name : {
43683             title: "name",
43684             width: 120
43685         },
43686         selectoptions : {
43687             title: "Options",
43688             width: 200
43689         }
43690     },
43691     
43692     // should we really allow this??
43693     // should this just be 
43694     'BODY' : {
43695         title : {
43696             title: "Title",
43697             width: 200,
43698             disabled : true
43699         }
43700     },
43701     'SPAN' : {
43702         'font-family'  : {
43703             title : "Font",
43704             style : 'fontFamily',
43705             displayField: 'display',
43706             optname : 'font-family',
43707             width: 140
43708         }
43709     },
43710     'DIV' : {
43711         'font-family'  : {
43712             title : "Font",
43713             style : 'fontFamily',
43714             displayField: 'display',
43715             optname : 'font-family',
43716             width: 140
43717         }
43718     },
43719      'P' : {
43720         'font-family'  : {
43721             title : "Font",
43722             style : 'fontFamily',
43723             displayField: 'display',
43724             optname : 'font-family',
43725             width: 140
43726         }
43727     },
43728     
43729     '*' : {
43730         // empty..
43731     }
43732
43733 };
43734
43735 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43736 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43737
43738 Roo.form.HtmlEditor.ToolbarContext.options = {
43739         'font-family'  : [ 
43740                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43741                 [ 'Courier New', 'Courier New'],
43742                 [ 'Tahoma', 'Tahoma'],
43743                 [ 'Times New Roman,serif', 'Times'],
43744                 [ 'Verdana','Verdana' ]
43745         ]
43746 };
43747
43748 // fixme - these need to be configurable..
43749  
43750
43751 Roo.form.HtmlEditor.ToolbarContext.types
43752
43753
43754 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43755     
43756     tb: false,
43757     
43758     rendered: false,
43759     
43760     editor : false,
43761     editorcore : false,
43762     /**
43763      * @cfg {Object} disable  List of toolbar elements to disable
43764          
43765      */
43766     disable : false,
43767     /**
43768      * @cfg {Object} styles List of styles 
43769      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43770      *
43771      * These must be defined in the page, so they get rendered correctly..
43772      * .headline { }
43773      * TD.underline { }
43774      * 
43775      */
43776     styles : false,
43777     
43778     options: false,
43779     
43780     toolbars : false,
43781     
43782     init : function(editor)
43783     {
43784         this.editor = editor;
43785         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43786         var editorcore = this.editorcore;
43787         
43788         var fid = editorcore.frameId;
43789         var etb = this;
43790         function btn(id, toggle, handler){
43791             var xid = fid + '-'+ id ;
43792             return {
43793                 id : xid,
43794                 cmd : id,
43795                 cls : 'x-btn-icon x-edit-'+id,
43796                 enableToggle:toggle !== false,
43797                 scope: editorcore, // was editor...
43798                 handler:handler||editorcore.relayBtnCmd,
43799                 clickEvent:'mousedown',
43800                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43801                 tabIndex:-1
43802             };
43803         }
43804         // create a new element.
43805         var wdiv = editor.wrap.createChild({
43806                 tag: 'div'
43807             }, editor.wrap.dom.firstChild.nextSibling, true);
43808         
43809         // can we do this more than once??
43810         
43811          // stop form submits
43812       
43813  
43814         // disable everything...
43815         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43816         this.toolbars = {};
43817            
43818         for (var i in  ty) {
43819           
43820             this.toolbars[i] = this.buildToolbar(ty[i],i);
43821         }
43822         this.tb = this.toolbars.BODY;
43823         this.tb.el.show();
43824         this.buildFooter();
43825         this.footer.show();
43826         editor.on('hide', function( ) { this.footer.hide() }, this);
43827         editor.on('show', function( ) { this.footer.show() }, this);
43828         
43829          
43830         this.rendered = true;
43831         
43832         // the all the btns;
43833         editor.on('editorevent', this.updateToolbar, this);
43834         // other toolbars need to implement this..
43835         //editor.on('editmodechange', this.updateToolbar, this);
43836     },
43837     
43838     
43839     
43840     /**
43841      * Protected method that will not generally be called directly. It triggers
43842      * a toolbar update by reading the markup state of the current selection in the editor.
43843      */
43844     updateToolbar: function(editor,ev,sel){
43845
43846         //Roo.log(ev);
43847         // capture mouse up - this is handy for selecting images..
43848         // perhaps should go somewhere else...
43849         if(!this.editorcore.activated){
43850              this.editor.onFirstFocus();
43851             return;
43852         }
43853         
43854         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43855         // selectNode - might want to handle IE?
43856         if (ev &&
43857             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43858             ev.target && ev.target.tagName == 'IMG') {
43859             // they have click on an image...
43860             // let's see if we can change the selection...
43861             sel = ev.target;
43862          
43863               var nodeRange = sel.ownerDocument.createRange();
43864             try {
43865                 nodeRange.selectNode(sel);
43866             } catch (e) {
43867                 nodeRange.selectNodeContents(sel);
43868             }
43869             //nodeRange.collapse(true);
43870             var s = this.editorcore.win.getSelection();
43871             s.removeAllRanges();
43872             s.addRange(nodeRange);
43873         }  
43874         
43875       
43876         var updateFooter = sel ? false : true;
43877         
43878         
43879         var ans = this.editorcore.getAllAncestors();
43880         
43881         // pick
43882         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43883         
43884         if (!sel) { 
43885             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43886             sel = sel ? sel : this.editorcore.doc.body;
43887             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43888             
43889         }
43890         // pick a menu that exists..
43891         var tn = sel.tagName.toUpperCase();
43892         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43893         
43894         tn = sel.tagName.toUpperCase();
43895         
43896         var lastSel = this.tb.selectedNode
43897         
43898         this.tb.selectedNode = sel;
43899         
43900         // if current menu does not match..
43901         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43902                 
43903             this.tb.el.hide();
43904             ///console.log("show: " + tn);
43905             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43906             this.tb.el.show();
43907             // update name
43908             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43909             
43910             
43911             // update attributes
43912             if (this.tb.fields) {
43913                 this.tb.fields.each(function(e) {
43914                     if (e.stylename) {
43915                         e.setValue(sel.style[e.stylename]);
43916                         return;
43917                     } 
43918                    e.setValue(sel.getAttribute(e.attrname));
43919                 });
43920             }
43921             
43922             var hasStyles = false;
43923             for(var i in this.styles) {
43924                 hasStyles = true;
43925                 break;
43926             }
43927             
43928             // update styles
43929             if (hasStyles) { 
43930                 var st = this.tb.fields.item(0);
43931                 
43932                 st.store.removeAll();
43933                
43934                 
43935                 var cn = sel.className.split(/\s+/);
43936                 
43937                 var avs = [];
43938                 if (this.styles['*']) {
43939                     
43940                     Roo.each(this.styles['*'], function(v) {
43941                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43942                     });
43943                 }
43944                 if (this.styles[tn]) { 
43945                     Roo.each(this.styles[tn], function(v) {
43946                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43947                     });
43948                 }
43949                 
43950                 st.store.loadData(avs);
43951                 st.collapse();
43952                 st.setValue(cn);
43953             }
43954             // flag our selected Node.
43955             this.tb.selectedNode = sel;
43956            
43957            
43958             Roo.menu.MenuMgr.hideAll();
43959
43960         }
43961         
43962         if (!updateFooter) {
43963             //this.footDisp.dom.innerHTML = ''; 
43964             return;
43965         }
43966         // update the footer
43967         //
43968         var html = '';
43969         
43970         this.footerEls = ans.reverse();
43971         Roo.each(this.footerEls, function(a,i) {
43972             if (!a) { return; }
43973             html += html.length ? ' &gt; '  :  '';
43974             
43975             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43976             
43977         });
43978        
43979         // 
43980         var sz = this.footDisp.up('td').getSize();
43981         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43982         this.footDisp.dom.style.marginLeft = '5px';
43983         
43984         this.footDisp.dom.style.overflow = 'hidden';
43985         
43986         this.footDisp.dom.innerHTML = html;
43987             
43988         //this.editorsyncValue();
43989     },
43990      
43991     
43992    
43993        
43994     // private
43995     onDestroy : function(){
43996         if(this.rendered){
43997             
43998             this.tb.items.each(function(item){
43999                 if(item.menu){
44000                     item.menu.removeAll();
44001                     if(item.menu.el){
44002                         item.menu.el.destroy();
44003                     }
44004                 }
44005                 item.destroy();
44006             });
44007              
44008         }
44009     },
44010     onFirstFocus: function() {
44011         // need to do this for all the toolbars..
44012         this.tb.items.each(function(item){
44013            item.enable();
44014         });
44015     },
44016     buildToolbar: function(tlist, nm)
44017     {
44018         var editor = this.editor;
44019         var editorcore = this.editorcore;
44020          // create a new element.
44021         var wdiv = editor.wrap.createChild({
44022                 tag: 'div'
44023             }, editor.wrap.dom.firstChild.nextSibling, true);
44024         
44025        
44026         var tb = new Roo.Toolbar(wdiv);
44027         // add the name..
44028         
44029         tb.add(nm+ ":&nbsp;");
44030         
44031         var styles = [];
44032         for(var i in this.styles) {
44033             styles.push(i);
44034         }
44035         
44036         // styles...
44037         if (styles && styles.length) {
44038             
44039             // this needs a multi-select checkbox...
44040             tb.addField( new Roo.form.ComboBox({
44041                 store: new Roo.data.SimpleStore({
44042                     id : 'val',
44043                     fields: ['val', 'selected'],
44044                     data : [] 
44045                 }),
44046                 name : '-roo-edit-className',
44047                 attrname : 'className',
44048                 displayField: 'val',
44049                 typeAhead: false,
44050                 mode: 'local',
44051                 editable : false,
44052                 triggerAction: 'all',
44053                 emptyText:'Select Style',
44054                 selectOnFocus:true,
44055                 width: 130,
44056                 listeners : {
44057                     'select': function(c, r, i) {
44058                         // initial support only for on class per el..
44059                         tb.selectedNode.className =  r ? r.get('val') : '';
44060                         editorcore.syncValue();
44061                     }
44062                 }
44063     
44064             }));
44065         }
44066         
44067         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44068         var tbops = tbc.options;
44069         
44070         for (var i in tlist) {
44071             
44072             var item = tlist[i];
44073             tb.add(item.title + ":&nbsp;");
44074             
44075             
44076             //optname == used so you can configure the options available..
44077             var opts = item.opts ? item.opts : false;
44078             if (item.optname) {
44079                 opts = tbops[item.optname];
44080            
44081             }
44082             
44083             if (opts) {
44084                 // opts == pulldown..
44085                 tb.addField( new Roo.form.ComboBox({
44086                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44087                         id : 'val',
44088                         fields: ['val', 'display'],
44089                         data : opts  
44090                     }),
44091                     name : '-roo-edit-' + i,
44092                     attrname : i,
44093                     stylename : item.style ? item.style : false,
44094                     displayField: item.displayField ? item.displayField : 'val',
44095                     valueField :  'val',
44096                     typeAhead: false,
44097                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44098                     editable : false,
44099                     triggerAction: 'all',
44100                     emptyText:'Select',
44101                     selectOnFocus:true,
44102                     width: item.width ? item.width  : 130,
44103                     listeners : {
44104                         'select': function(c, r, i) {
44105                             if (c.stylename) {
44106                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44107                                 return;
44108                             }
44109                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44110                         }
44111                     }
44112
44113                 }));
44114                 continue;
44115                     
44116                  
44117                 
44118                 tb.addField( new Roo.form.TextField({
44119                     name: i,
44120                     width: 100,
44121                     //allowBlank:false,
44122                     value: ''
44123                 }));
44124                 continue;
44125             }
44126             tb.addField( new Roo.form.TextField({
44127                 name: '-roo-edit-' + i,
44128                 attrname : i,
44129                 
44130                 width: item.width,
44131                 //allowBlank:true,
44132                 value: '',
44133                 listeners: {
44134                     'change' : function(f, nv, ov) {
44135                         tb.selectedNode.setAttribute(f.attrname, nv);
44136                     }
44137                 }
44138             }));
44139              
44140         }
44141         tb.addFill();
44142         var _this = this;
44143         tb.addButton( {
44144             text: 'Remove Tag',
44145     
44146             listeners : {
44147                 click : function ()
44148                 {
44149                     // remove
44150                     // undo does not work.
44151                      
44152                     var sn = tb.selectedNode;
44153                     
44154                     var pn = sn.parentNode;
44155                     
44156                     var stn =  sn.childNodes[0];
44157                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44158                     while (sn.childNodes.length) {
44159                         var node = sn.childNodes[0];
44160                         sn.removeChild(node);
44161                         //Roo.log(node);
44162                         pn.insertBefore(node, sn);
44163                         
44164                     }
44165                     pn.removeChild(sn);
44166                     var range = editorcore.createRange();
44167         
44168                     range.setStart(stn,0);
44169                     range.setEnd(en,0); //????
44170                     //range.selectNode(sel);
44171                     
44172                     
44173                     var selection = editorcore.getSelection();
44174                     selection.removeAllRanges();
44175                     selection.addRange(range);
44176                     
44177                     
44178                     
44179                     //_this.updateToolbar(null, null, pn);
44180                     _this.updateToolbar(null, null, null);
44181                     _this.footDisp.dom.innerHTML = ''; 
44182                 }
44183             }
44184             
44185                     
44186                 
44187             
44188         });
44189         
44190         
44191         tb.el.on('click', function(e){
44192             e.preventDefault(); // what does this do?
44193         });
44194         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44195         tb.el.hide();
44196         tb.name = nm;
44197         // dont need to disable them... as they will get hidden
44198         return tb;
44199          
44200         
44201     },
44202     buildFooter : function()
44203     {
44204         
44205         var fel = this.editor.wrap.createChild();
44206         this.footer = new Roo.Toolbar(fel);
44207         // toolbar has scrolly on left / right?
44208         var footDisp= new Roo.Toolbar.Fill();
44209         var _t = this;
44210         this.footer.add(
44211             {
44212                 text : '&lt;',
44213                 xtype: 'Button',
44214                 handler : function() {
44215                     _t.footDisp.scrollTo('left',0,true)
44216                 }
44217             }
44218         );
44219         this.footer.add( footDisp );
44220         this.footer.add( 
44221             {
44222                 text : '&gt;',
44223                 xtype: 'Button',
44224                 handler : function() {
44225                     // no animation..
44226                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44227                 }
44228             }
44229         );
44230         var fel = Roo.get(footDisp.el);
44231         fel.addClass('x-editor-context');
44232         this.footDispWrap = fel; 
44233         this.footDispWrap.overflow  = 'hidden';
44234         
44235         this.footDisp = fel.createChild();
44236         this.footDispWrap.on('click', this.onContextClick, this)
44237         
44238         
44239     },
44240     onContextClick : function (ev,dom)
44241     {
44242         ev.preventDefault();
44243         var  cn = dom.className;
44244         //Roo.log(cn);
44245         if (!cn.match(/x-ed-loc-/)) {
44246             return;
44247         }
44248         var n = cn.split('-').pop();
44249         var ans = this.footerEls;
44250         var sel = ans[n];
44251         
44252          // pick
44253         var range = this.editorcore.createRange();
44254         
44255         range.selectNodeContents(sel);
44256         //range.selectNode(sel);
44257         
44258         
44259         var selection = this.editorcore.getSelection();
44260         selection.removeAllRanges();
44261         selection.addRange(range);
44262         
44263         
44264         
44265         this.updateToolbar(null, null, sel);
44266         
44267         
44268     }
44269     
44270     
44271     
44272     
44273     
44274 });
44275
44276
44277
44278
44279
44280 /*
44281  * Based on:
44282  * Ext JS Library 1.1.1
44283  * Copyright(c) 2006-2007, Ext JS, LLC.
44284  *
44285  * Originally Released Under LGPL - original licence link has changed is not relivant.
44286  *
44287  * Fork - LGPL
44288  * <script type="text/javascript">
44289  */
44290  
44291 /**
44292  * @class Roo.form.BasicForm
44293  * @extends Roo.util.Observable
44294  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44295  * @constructor
44296  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44297  * @param {Object} config Configuration options
44298  */
44299 Roo.form.BasicForm = function(el, config){
44300     this.allItems = [];
44301     this.childForms = [];
44302     Roo.apply(this, config);
44303     /*
44304      * The Roo.form.Field items in this form.
44305      * @type MixedCollection
44306      */
44307      
44308      
44309     this.items = new Roo.util.MixedCollection(false, function(o){
44310         return o.id || (o.id = Roo.id());
44311     });
44312     this.addEvents({
44313         /**
44314          * @event beforeaction
44315          * Fires before any action is performed. Return false to cancel the action.
44316          * @param {Form} this
44317          * @param {Action} action The action to be performed
44318          */
44319         beforeaction: true,
44320         /**
44321          * @event actionfailed
44322          * Fires when an action fails.
44323          * @param {Form} this
44324          * @param {Action} action The action that failed
44325          */
44326         actionfailed : true,
44327         /**
44328          * @event actioncomplete
44329          * Fires when an action is completed.
44330          * @param {Form} this
44331          * @param {Action} action The action that completed
44332          */
44333         actioncomplete : true
44334     });
44335     if(el){
44336         this.initEl(el);
44337     }
44338     Roo.form.BasicForm.superclass.constructor.call(this);
44339 };
44340
44341 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44342     /**
44343      * @cfg {String} method
44344      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44345      */
44346     /**
44347      * @cfg {DataReader} reader
44348      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44349      * This is optional as there is built-in support for processing JSON.
44350      */
44351     /**
44352      * @cfg {DataReader} errorReader
44353      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44354      * This is completely optional as there is built-in support for processing JSON.
44355      */
44356     /**
44357      * @cfg {String} url
44358      * The URL to use for form actions if one isn't supplied in the action options.
44359      */
44360     /**
44361      * @cfg {Boolean} fileUpload
44362      * Set to true if this form is a file upload.
44363      */
44364      
44365     /**
44366      * @cfg {Object} baseParams
44367      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44368      */
44369      /**
44370      
44371     /**
44372      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44373      */
44374     timeout: 30,
44375
44376     // private
44377     activeAction : null,
44378
44379     /**
44380      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44381      * or setValues() data instead of when the form was first created.
44382      */
44383     trackResetOnLoad : false,
44384     
44385     
44386     /**
44387      * childForms - used for multi-tab forms
44388      * @type {Array}
44389      */
44390     childForms : false,
44391     
44392     /**
44393      * allItems - full list of fields.
44394      * @type {Array}
44395      */
44396     allItems : false,
44397     
44398     /**
44399      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44400      * element by passing it or its id or mask the form itself by passing in true.
44401      * @type Mixed
44402      */
44403     waitMsgTarget : false,
44404
44405     // private
44406     initEl : function(el){
44407         this.el = Roo.get(el);
44408         this.id = this.el.id || Roo.id();
44409         this.el.on('submit', this.onSubmit, this);
44410         this.el.addClass('x-form');
44411     },
44412
44413     // private
44414     onSubmit : function(e){
44415         e.stopEvent();
44416     },
44417
44418     /**
44419      * Returns true if client-side validation on the form is successful.
44420      * @return Boolean
44421      */
44422     isValid : function(){
44423         var valid = true;
44424         this.items.each(function(f){
44425            if(!f.validate()){
44426                valid = false;
44427            }
44428         });
44429         return valid;
44430     },
44431
44432     /**
44433      * Returns true if any fields in this form have changed since their original load.
44434      * @return Boolean
44435      */
44436     isDirty : function(){
44437         var dirty = false;
44438         this.items.each(function(f){
44439            if(f.isDirty()){
44440                dirty = true;
44441                return false;
44442            }
44443         });
44444         return dirty;
44445     },
44446
44447     /**
44448      * Performs a predefined action (submit or load) or custom actions you define on this form.
44449      * @param {String} actionName The name of the action type
44450      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44451      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44452      * accept other config options):
44453      * <pre>
44454 Property          Type             Description
44455 ----------------  ---------------  ----------------------------------------------------------------------------------
44456 url               String           The url for the action (defaults to the form's url)
44457 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44458 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44459 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44460                                    validate the form on the client (defaults to false)
44461      * </pre>
44462      * @return {BasicForm} this
44463      */
44464     doAction : function(action, options){
44465         if(typeof action == 'string'){
44466             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44467         }
44468         if(this.fireEvent('beforeaction', this, action) !== false){
44469             this.beforeAction(action);
44470             action.run.defer(100, action);
44471         }
44472         return this;
44473     },
44474
44475     /**
44476      * Shortcut to do a submit action.
44477      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44478      * @return {BasicForm} this
44479      */
44480     submit : function(options){
44481         this.doAction('submit', options);
44482         return this;
44483     },
44484
44485     /**
44486      * Shortcut to do a load action.
44487      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44488      * @return {BasicForm} this
44489      */
44490     load : function(options){
44491         this.doAction('load', options);
44492         return this;
44493     },
44494
44495     /**
44496      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44497      * @param {Record} record The record to edit
44498      * @return {BasicForm} this
44499      */
44500     updateRecord : function(record){
44501         record.beginEdit();
44502         var fs = record.fields;
44503         fs.each(function(f){
44504             var field = this.findField(f.name);
44505             if(field){
44506                 record.set(f.name, field.getValue());
44507             }
44508         }, this);
44509         record.endEdit();
44510         return this;
44511     },
44512
44513     /**
44514      * Loads an Roo.data.Record into this form.
44515      * @param {Record} record The record to load
44516      * @return {BasicForm} this
44517      */
44518     loadRecord : function(record){
44519         this.setValues(record.data);
44520         return this;
44521     },
44522
44523     // private
44524     beforeAction : function(action){
44525         var o = action.options;
44526         
44527        
44528         if(this.waitMsgTarget === true){
44529             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44530         }else if(this.waitMsgTarget){
44531             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44532             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44533         }else {
44534             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44535         }
44536          
44537     },
44538
44539     // private
44540     afterAction : function(action, success){
44541         this.activeAction = null;
44542         var o = action.options;
44543         
44544         if(this.waitMsgTarget === true){
44545             this.el.unmask();
44546         }else if(this.waitMsgTarget){
44547             this.waitMsgTarget.unmask();
44548         }else{
44549             Roo.MessageBox.updateProgress(1);
44550             Roo.MessageBox.hide();
44551         }
44552          
44553         if(success){
44554             if(o.reset){
44555                 this.reset();
44556             }
44557             Roo.callback(o.success, o.scope, [this, action]);
44558             this.fireEvent('actioncomplete', this, action);
44559             
44560         }else{
44561             
44562             // failure condition..
44563             // we have a scenario where updates need confirming.
44564             // eg. if a locking scenario exists..
44565             // we look for { errors : { needs_confirm : true }} in the response.
44566             if (
44567                 (typeof(action.result) != 'undefined')  &&
44568                 (typeof(action.result.errors) != 'undefined')  &&
44569                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44570            ){
44571                 var _t = this;
44572                 Roo.MessageBox.confirm(
44573                     "Change requires confirmation",
44574                     action.result.errorMsg,
44575                     function(r) {
44576                         if (r != 'yes') {
44577                             return;
44578                         }
44579                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44580                     }
44581                     
44582                 );
44583                 
44584                 
44585                 
44586                 return;
44587             }
44588             
44589             Roo.callback(o.failure, o.scope, [this, action]);
44590             // show an error message if no failed handler is set..
44591             if (!this.hasListener('actionfailed')) {
44592                 Roo.MessageBox.alert("Error",
44593                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44594                         action.result.errorMsg :
44595                         "Saving Failed, please check your entries or try again"
44596                 );
44597             }
44598             
44599             this.fireEvent('actionfailed', this, action);
44600         }
44601         
44602     },
44603
44604     /**
44605      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44606      * @param {String} id The value to search for
44607      * @return Field
44608      */
44609     findField : function(id){
44610         var field = this.items.get(id);
44611         if(!field){
44612             this.items.each(function(f){
44613                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44614                     field = f;
44615                     return false;
44616                 }
44617             });
44618         }
44619         return field || null;
44620     },
44621
44622     /**
44623      * Add a secondary form to this one, 
44624      * Used to provide tabbed forms. One form is primary, with hidden values 
44625      * which mirror the elements from the other forms.
44626      * 
44627      * @param {Roo.form.Form} form to add.
44628      * 
44629      */
44630     addForm : function(form)
44631     {
44632        
44633         if (this.childForms.indexOf(form) > -1) {
44634             // already added..
44635             return;
44636         }
44637         this.childForms.push(form);
44638         var n = '';
44639         Roo.each(form.allItems, function (fe) {
44640             
44641             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44642             if (this.findField(n)) { // already added..
44643                 return;
44644             }
44645             var add = new Roo.form.Hidden({
44646                 name : n
44647             });
44648             add.render(this.el);
44649             
44650             this.add( add );
44651         }, this);
44652         
44653     },
44654     /**
44655      * Mark fields in this form invalid in bulk.
44656      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44657      * @return {BasicForm} this
44658      */
44659     markInvalid : function(errors){
44660         if(errors instanceof Array){
44661             for(var i = 0, len = errors.length; i < len; i++){
44662                 var fieldError = errors[i];
44663                 var f = this.findField(fieldError.id);
44664                 if(f){
44665                     f.markInvalid(fieldError.msg);
44666                 }
44667             }
44668         }else{
44669             var field, id;
44670             for(id in errors){
44671                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44672                     field.markInvalid(errors[id]);
44673                 }
44674             }
44675         }
44676         Roo.each(this.childForms || [], function (f) {
44677             f.markInvalid(errors);
44678         });
44679         
44680         return this;
44681     },
44682
44683     /**
44684      * Set values for fields in this form in bulk.
44685      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44686      * @return {BasicForm} this
44687      */
44688     setValues : function(values){
44689         if(values instanceof Array){ // array of objects
44690             for(var i = 0, len = values.length; i < len; i++){
44691                 var v = values[i];
44692                 var f = this.findField(v.id);
44693                 if(f){
44694                     f.setValue(v.value);
44695                     if(this.trackResetOnLoad){
44696                         f.originalValue = f.getValue();
44697                     }
44698                 }
44699             }
44700         }else{ // object hash
44701             var field, id;
44702             for(id in values){
44703                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44704                     
44705                     if (field.setFromData && 
44706                         field.valueField && 
44707                         field.displayField &&
44708                         // combos' with local stores can 
44709                         // be queried via setValue()
44710                         // to set their value..
44711                         (field.store && !field.store.isLocal)
44712                         ) {
44713                         // it's a combo
44714                         var sd = { };
44715                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44716                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44717                         field.setFromData(sd);
44718                         
44719                     } else {
44720                         field.setValue(values[id]);
44721                     }
44722                     
44723                     
44724                     if(this.trackResetOnLoad){
44725                         field.originalValue = field.getValue();
44726                     }
44727                 }
44728             }
44729         }
44730          
44731         Roo.each(this.childForms || [], function (f) {
44732             f.setValues(values);
44733         });
44734                 
44735         return this;
44736     },
44737
44738     /**
44739      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44740      * they are returned as an array.
44741      * @param {Boolean} asString
44742      * @return {Object}
44743      */
44744     getValues : function(asString){
44745         if (this.childForms) {
44746             // copy values from the child forms
44747             Roo.each(this.childForms, function (f) {
44748                 this.setValues(f.getValues());
44749             }, this);
44750         }
44751         
44752         
44753         
44754         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44755         if(asString === true){
44756             return fs;
44757         }
44758         return Roo.urlDecode(fs);
44759     },
44760     
44761     /**
44762      * Returns the fields in this form as an object with key/value pairs. 
44763      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44764      * @return {Object}
44765      */
44766     getFieldValues : function(with_hidden)
44767     {
44768         if (this.childForms) {
44769             // copy values from the child forms
44770             // should this call getFieldValues - probably not as we do not currently copy
44771             // hidden fields when we generate..
44772             Roo.each(this.childForms, function (f) {
44773                 this.setValues(f.getValues());
44774             }, this);
44775         }
44776         
44777         var ret = {};
44778         this.items.each(function(f){
44779             if (!f.getName()) {
44780                 return;
44781             }
44782             var v = f.getValue();
44783             if (f.inputType =='radio') {
44784                 if (typeof(ret[f.getName()]) == 'undefined') {
44785                     ret[f.getName()] = ''; // empty..
44786                 }
44787                 
44788                 if (!f.el.dom.checked) {
44789                     return;
44790                     
44791                 }
44792                 v = f.el.dom.value;
44793                 
44794             }
44795             
44796             // not sure if this supported any more..
44797             if ((typeof(v) == 'object') && f.getRawValue) {
44798                 v = f.getRawValue() ; // dates..
44799             }
44800             // combo boxes where name != hiddenName...
44801             if (f.name != f.getName()) {
44802                 ret[f.name] = f.getRawValue();
44803             }
44804             ret[f.getName()] = v;
44805         });
44806         
44807         return ret;
44808     },
44809
44810     /**
44811      * Clears all invalid messages in this form.
44812      * @return {BasicForm} this
44813      */
44814     clearInvalid : function(){
44815         this.items.each(function(f){
44816            f.clearInvalid();
44817         });
44818         
44819         Roo.each(this.childForms || [], function (f) {
44820             f.clearInvalid();
44821         });
44822         
44823         
44824         return this;
44825     },
44826
44827     /**
44828      * Resets this form.
44829      * @return {BasicForm} this
44830      */
44831     reset : function(){
44832         this.items.each(function(f){
44833             f.reset();
44834         });
44835         
44836         Roo.each(this.childForms || [], function (f) {
44837             f.reset();
44838         });
44839        
44840         
44841         return this;
44842     },
44843
44844     /**
44845      * Add Roo.form components to this form.
44846      * @param {Field} field1
44847      * @param {Field} field2 (optional)
44848      * @param {Field} etc (optional)
44849      * @return {BasicForm} this
44850      */
44851     add : function(){
44852         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44853         return this;
44854     },
44855
44856
44857     /**
44858      * Removes a field from the items collection (does NOT remove its markup).
44859      * @param {Field} field
44860      * @return {BasicForm} this
44861      */
44862     remove : function(field){
44863         this.items.remove(field);
44864         return this;
44865     },
44866
44867     /**
44868      * Looks at the fields in this form, checks them for an id attribute,
44869      * and calls applyTo on the existing dom element with that id.
44870      * @return {BasicForm} this
44871      */
44872     render : function(){
44873         this.items.each(function(f){
44874             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44875                 f.applyTo(f.id);
44876             }
44877         });
44878         return this;
44879     },
44880
44881     /**
44882      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44883      * @param {Object} values
44884      * @return {BasicForm} this
44885      */
44886     applyToFields : function(o){
44887         this.items.each(function(f){
44888            Roo.apply(f, o);
44889         });
44890         return this;
44891     },
44892
44893     /**
44894      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44895      * @param {Object} values
44896      * @return {BasicForm} this
44897      */
44898     applyIfToFields : function(o){
44899         this.items.each(function(f){
44900            Roo.applyIf(f, o);
44901         });
44902         return this;
44903     }
44904 });
44905
44906 // back compat
44907 Roo.BasicForm = Roo.form.BasicForm;/*
44908  * Based on:
44909  * Ext JS Library 1.1.1
44910  * Copyright(c) 2006-2007, Ext JS, LLC.
44911  *
44912  * Originally Released Under LGPL - original licence link has changed is not relivant.
44913  *
44914  * Fork - LGPL
44915  * <script type="text/javascript">
44916  */
44917
44918 /**
44919  * @class Roo.form.Form
44920  * @extends Roo.form.BasicForm
44921  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44922  * @constructor
44923  * @param {Object} config Configuration options
44924  */
44925 Roo.form.Form = function(config){
44926     var xitems =  [];
44927     if (config.items) {
44928         xitems = config.items;
44929         delete config.items;
44930     }
44931    
44932     
44933     Roo.form.Form.superclass.constructor.call(this, null, config);
44934     this.url = this.url || this.action;
44935     if(!this.root){
44936         this.root = new Roo.form.Layout(Roo.applyIf({
44937             id: Roo.id()
44938         }, config));
44939     }
44940     this.active = this.root;
44941     /**
44942      * Array of all the buttons that have been added to this form via {@link addButton}
44943      * @type Array
44944      */
44945     this.buttons = [];
44946     this.allItems = [];
44947     this.addEvents({
44948         /**
44949          * @event clientvalidation
44950          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44951          * @param {Form} this
44952          * @param {Boolean} valid true if the form has passed client-side validation
44953          */
44954         clientvalidation: true,
44955         /**
44956          * @event rendered
44957          * Fires when the form is rendered
44958          * @param {Roo.form.Form} form
44959          */
44960         rendered : true
44961     });
44962     
44963     if (this.progressUrl) {
44964             // push a hidden field onto the list of fields..
44965             this.addxtype( {
44966                     xns: Roo.form, 
44967                     xtype : 'Hidden', 
44968                     name : 'UPLOAD_IDENTIFIER' 
44969             });
44970         }
44971         
44972     
44973     Roo.each(xitems, this.addxtype, this);
44974     
44975     
44976     
44977 };
44978
44979 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44980     /**
44981      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44982      */
44983     /**
44984      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44985      */
44986     /**
44987      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44988      */
44989     buttonAlign:'center',
44990
44991     /**
44992      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44993      */
44994     minButtonWidth:75,
44995
44996     /**
44997      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44998      * This property cascades to child containers if not set.
44999      */
45000     labelAlign:'left',
45001
45002     /**
45003      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45004      * fires a looping event with that state. This is required to bind buttons to the valid
45005      * state using the config value formBind:true on the button.
45006      */
45007     monitorValid : false,
45008
45009     /**
45010      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45011      */
45012     monitorPoll : 200,
45013     
45014     /**
45015      * @cfg {String} progressUrl - Url to return progress data 
45016      */
45017     
45018     progressUrl : false,
45019   
45020     /**
45021      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45022      * fields are added and the column is closed. If no fields are passed the column remains open
45023      * until end() is called.
45024      * @param {Object} config The config to pass to the column
45025      * @param {Field} field1 (optional)
45026      * @param {Field} field2 (optional)
45027      * @param {Field} etc (optional)
45028      * @return Column The column container object
45029      */
45030     column : function(c){
45031         var col = new Roo.form.Column(c);
45032         this.start(col);
45033         if(arguments.length > 1){ // duplicate code required because of Opera
45034             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45035             this.end();
45036         }
45037         return col;
45038     },
45039
45040     /**
45041      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45042      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45043      * until end() is called.
45044      * @param {Object} config The config to pass to the fieldset
45045      * @param {Field} field1 (optional)
45046      * @param {Field} field2 (optional)
45047      * @param {Field} etc (optional)
45048      * @return FieldSet The fieldset container object
45049      */
45050     fieldset : function(c){
45051         var fs = new Roo.form.FieldSet(c);
45052         this.start(fs);
45053         if(arguments.length > 1){ // duplicate code required because of Opera
45054             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45055             this.end();
45056         }
45057         return fs;
45058     },
45059
45060     /**
45061      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45062      * fields are added and the container is closed. If no fields are passed the container remains open
45063      * until end() is called.
45064      * @param {Object} config The config to pass to the Layout
45065      * @param {Field} field1 (optional)
45066      * @param {Field} field2 (optional)
45067      * @param {Field} etc (optional)
45068      * @return Layout The container object
45069      */
45070     container : function(c){
45071         var l = new Roo.form.Layout(c);
45072         this.start(l);
45073         if(arguments.length > 1){ // duplicate code required because of Opera
45074             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45075             this.end();
45076         }
45077         return l;
45078     },
45079
45080     /**
45081      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45082      * @param {Object} container A Roo.form.Layout or subclass of Layout
45083      * @return {Form} this
45084      */
45085     start : function(c){
45086         // cascade label info
45087         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45088         this.active.stack.push(c);
45089         c.ownerCt = this.active;
45090         this.active = c;
45091         return this;
45092     },
45093
45094     /**
45095      * Closes the current open container
45096      * @return {Form} this
45097      */
45098     end : function(){
45099         if(this.active == this.root){
45100             return this;
45101         }
45102         this.active = this.active.ownerCt;
45103         return this;
45104     },
45105
45106     /**
45107      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45108      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45109      * as the label of the field.
45110      * @param {Field} field1
45111      * @param {Field} field2 (optional)
45112      * @param {Field} etc. (optional)
45113      * @return {Form} this
45114      */
45115     add : function(){
45116         this.active.stack.push.apply(this.active.stack, arguments);
45117         this.allItems.push.apply(this.allItems,arguments);
45118         var r = [];
45119         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45120             if(a[i].isFormField){
45121                 r.push(a[i]);
45122             }
45123         }
45124         if(r.length > 0){
45125             Roo.form.Form.superclass.add.apply(this, r);
45126         }
45127         return this;
45128     },
45129     
45130
45131     
45132     
45133     
45134      /**
45135      * Find any element that has been added to a form, using it's ID or name
45136      * This can include framesets, columns etc. along with regular fields..
45137      * @param {String} id - id or name to find.
45138      
45139      * @return {Element} e - or false if nothing found.
45140      */
45141     findbyId : function(id)
45142     {
45143         var ret = false;
45144         if (!id) {
45145             return ret;
45146         }
45147         Roo.each(this.allItems, function(f){
45148             if (f.id == id || f.name == id ){
45149                 ret = f;
45150                 return false;
45151             }
45152         });
45153         return ret;
45154     },
45155
45156     
45157     
45158     /**
45159      * Render this form into the passed container. This should only be called once!
45160      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45161      * @return {Form} this
45162      */
45163     render : function(ct)
45164     {
45165         
45166         
45167         
45168         ct = Roo.get(ct);
45169         var o = this.autoCreate || {
45170             tag: 'form',
45171             method : this.method || 'POST',
45172             id : this.id || Roo.id()
45173         };
45174         this.initEl(ct.createChild(o));
45175
45176         this.root.render(this.el);
45177         
45178        
45179              
45180         this.items.each(function(f){
45181             f.render('x-form-el-'+f.id);
45182         });
45183
45184         if(this.buttons.length > 0){
45185             // tables are required to maintain order and for correct IE layout
45186             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45187                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45188                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45189             }}, null, true);
45190             var tr = tb.getElementsByTagName('tr')[0];
45191             for(var i = 0, len = this.buttons.length; i < len; i++) {
45192                 var b = this.buttons[i];
45193                 var td = document.createElement('td');
45194                 td.className = 'x-form-btn-td';
45195                 b.render(tr.appendChild(td));
45196             }
45197         }
45198         if(this.monitorValid){ // initialize after render
45199             this.startMonitoring();
45200         }
45201         this.fireEvent('rendered', this);
45202         return this;
45203     },
45204
45205     /**
45206      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45207      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45208      * object or a valid Roo.DomHelper element config
45209      * @param {Function} handler The function called when the button is clicked
45210      * @param {Object} scope (optional) The scope of the handler function
45211      * @return {Roo.Button}
45212      */
45213     addButton : function(config, handler, scope){
45214         var bc = {
45215             handler: handler,
45216             scope: scope,
45217             minWidth: this.minButtonWidth,
45218             hideParent:true
45219         };
45220         if(typeof config == "string"){
45221             bc.text = config;
45222         }else{
45223             Roo.apply(bc, config);
45224         }
45225         var btn = new Roo.Button(null, bc);
45226         this.buttons.push(btn);
45227         return btn;
45228     },
45229
45230      /**
45231      * Adds a series of form elements (using the xtype property as the factory method.
45232      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45233      * @param {Object} config 
45234      */
45235     
45236     addxtype : function()
45237     {
45238         var ar = Array.prototype.slice.call(arguments, 0);
45239         var ret = false;
45240         for(var i = 0; i < ar.length; i++) {
45241             if (!ar[i]) {
45242                 continue; // skip -- if this happends something invalid got sent, we 
45243                 // should ignore it, as basically that interface element will not show up
45244                 // and that should be pretty obvious!!
45245             }
45246             
45247             if (Roo.form[ar[i].xtype]) {
45248                 ar[i].form = this;
45249                 var fe = Roo.factory(ar[i], Roo.form);
45250                 if (!ret) {
45251                     ret = fe;
45252                 }
45253                 fe.form = this;
45254                 if (fe.store) {
45255                     fe.store.form = this;
45256                 }
45257                 if (fe.isLayout) {  
45258                          
45259                     this.start(fe);
45260                     this.allItems.push(fe);
45261                     if (fe.items && fe.addxtype) {
45262                         fe.addxtype.apply(fe, fe.items);
45263                         delete fe.items;
45264                     }
45265                      this.end();
45266                     continue;
45267                 }
45268                 
45269                 
45270                  
45271                 this.add(fe);
45272               //  console.log('adding ' + ar[i].xtype);
45273             }
45274             if (ar[i].xtype == 'Button') {  
45275                 //console.log('adding button');
45276                 //console.log(ar[i]);
45277                 this.addButton(ar[i]);
45278                 this.allItems.push(fe);
45279                 continue;
45280             }
45281             
45282             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45283                 alert('end is not supported on xtype any more, use items');
45284             //    this.end();
45285             //    //console.log('adding end');
45286             }
45287             
45288         }
45289         return ret;
45290     },
45291     
45292     /**
45293      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45294      * option "monitorValid"
45295      */
45296     startMonitoring : function(){
45297         if(!this.bound){
45298             this.bound = true;
45299             Roo.TaskMgr.start({
45300                 run : this.bindHandler,
45301                 interval : this.monitorPoll || 200,
45302                 scope: this
45303             });
45304         }
45305     },
45306
45307     /**
45308      * Stops monitoring of the valid state of this form
45309      */
45310     stopMonitoring : function(){
45311         this.bound = false;
45312     },
45313
45314     // private
45315     bindHandler : function(){
45316         if(!this.bound){
45317             return false; // stops binding
45318         }
45319         var valid = true;
45320         this.items.each(function(f){
45321             if(!f.isValid(true)){
45322                 valid = false;
45323                 return false;
45324             }
45325         });
45326         for(var i = 0, len = this.buttons.length; i < len; i++){
45327             var btn = this.buttons[i];
45328             if(btn.formBind === true && btn.disabled === valid){
45329                 btn.setDisabled(!valid);
45330             }
45331         }
45332         this.fireEvent('clientvalidation', this, valid);
45333     }
45334     
45335     
45336     
45337     
45338     
45339     
45340     
45341     
45342 });
45343
45344
45345 // back compat
45346 Roo.Form = Roo.form.Form;
45347 /*
45348  * Based on:
45349  * Ext JS Library 1.1.1
45350  * Copyright(c) 2006-2007, Ext JS, LLC.
45351  *
45352  * Originally Released Under LGPL - original licence link has changed is not relivant.
45353  *
45354  * Fork - LGPL
45355  * <script type="text/javascript">
45356  */
45357
45358 // as we use this in bootstrap.
45359 Roo.namespace('Roo.form');
45360  /**
45361  * @class Roo.form.Action
45362  * Internal Class used to handle form actions
45363  * @constructor
45364  * @param {Roo.form.BasicForm} el The form element or its id
45365  * @param {Object} config Configuration options
45366  */
45367
45368  
45369  
45370 // define the action interface
45371 Roo.form.Action = function(form, options){
45372     this.form = form;
45373     this.options = options || {};
45374 };
45375 /**
45376  * Client Validation Failed
45377  * @const 
45378  */
45379 Roo.form.Action.CLIENT_INVALID = 'client';
45380 /**
45381  * Server Validation Failed
45382  * @const 
45383  */
45384 Roo.form.Action.SERVER_INVALID = 'server';
45385  /**
45386  * Connect to Server Failed
45387  * @const 
45388  */
45389 Roo.form.Action.CONNECT_FAILURE = 'connect';
45390 /**
45391  * Reading Data from Server Failed
45392  * @const 
45393  */
45394 Roo.form.Action.LOAD_FAILURE = 'load';
45395
45396 Roo.form.Action.prototype = {
45397     type : 'default',
45398     failureType : undefined,
45399     response : undefined,
45400     result : undefined,
45401
45402     // interface method
45403     run : function(options){
45404
45405     },
45406
45407     // interface method
45408     success : function(response){
45409
45410     },
45411
45412     // interface method
45413     handleResponse : function(response){
45414
45415     },
45416
45417     // default connection failure
45418     failure : function(response){
45419         
45420         this.response = response;
45421         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45422         this.form.afterAction(this, false);
45423     },
45424
45425     processResponse : function(response){
45426         this.response = response;
45427         if(!response.responseText){
45428             return true;
45429         }
45430         this.result = this.handleResponse(response);
45431         return this.result;
45432     },
45433
45434     // utility functions used internally
45435     getUrl : function(appendParams){
45436         var url = this.options.url || this.form.url || this.form.el.dom.action;
45437         if(appendParams){
45438             var p = this.getParams();
45439             if(p){
45440                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45441             }
45442         }
45443         return url;
45444     },
45445
45446     getMethod : function(){
45447         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45448     },
45449
45450     getParams : function(){
45451         var bp = this.form.baseParams;
45452         var p = this.options.params;
45453         if(p){
45454             if(typeof p == "object"){
45455                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45456             }else if(typeof p == 'string' && bp){
45457                 p += '&' + Roo.urlEncode(bp);
45458             }
45459         }else if(bp){
45460             p = Roo.urlEncode(bp);
45461         }
45462         return p;
45463     },
45464
45465     createCallback : function(){
45466         return {
45467             success: this.success,
45468             failure: this.failure,
45469             scope: this,
45470             timeout: (this.form.timeout*1000),
45471             upload: this.form.fileUpload ? this.success : undefined
45472         };
45473     }
45474 };
45475
45476 Roo.form.Action.Submit = function(form, options){
45477     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45478 };
45479
45480 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45481     type : 'submit',
45482
45483     haveProgress : false,
45484     uploadComplete : false,
45485     
45486     // uploadProgress indicator.
45487     uploadProgress : function()
45488     {
45489         if (!this.form.progressUrl) {
45490             return;
45491         }
45492         
45493         if (!this.haveProgress) {
45494             Roo.MessageBox.progress("Uploading", "Uploading");
45495         }
45496         if (this.uploadComplete) {
45497            Roo.MessageBox.hide();
45498            return;
45499         }
45500         
45501         this.haveProgress = true;
45502    
45503         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45504         
45505         var c = new Roo.data.Connection();
45506         c.request({
45507             url : this.form.progressUrl,
45508             params: {
45509                 id : uid
45510             },
45511             method: 'GET',
45512             success : function(req){
45513                //console.log(data);
45514                 var rdata = false;
45515                 var edata;
45516                 try  {
45517                    rdata = Roo.decode(req.responseText)
45518                 } catch (e) {
45519                     Roo.log("Invalid data from server..");
45520                     Roo.log(edata);
45521                     return;
45522                 }
45523                 if (!rdata || !rdata.success) {
45524                     Roo.log(rdata);
45525                     Roo.MessageBox.alert(Roo.encode(rdata));
45526                     return;
45527                 }
45528                 var data = rdata.data;
45529                 
45530                 if (this.uploadComplete) {
45531                    Roo.MessageBox.hide();
45532                    return;
45533                 }
45534                    
45535                 if (data){
45536                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45537                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45538                     );
45539                 }
45540                 this.uploadProgress.defer(2000,this);
45541             },
45542        
45543             failure: function(data) {
45544                 Roo.log('progress url failed ');
45545                 Roo.log(data);
45546             },
45547             scope : this
45548         });
45549            
45550     },
45551     
45552     
45553     run : function()
45554     {
45555         // run get Values on the form, so it syncs any secondary forms.
45556         this.form.getValues();
45557         
45558         var o = this.options;
45559         var method = this.getMethod();
45560         var isPost = method == 'POST';
45561         if(o.clientValidation === false || this.form.isValid()){
45562             
45563             if (this.form.progressUrl) {
45564                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45565                     (new Date() * 1) + '' + Math.random());
45566                     
45567             } 
45568             
45569             
45570             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45571                 form:this.form.el.dom,
45572                 url:this.getUrl(!isPost),
45573                 method: method,
45574                 params:isPost ? this.getParams() : null,
45575                 isUpload: this.form.fileUpload
45576             }));
45577             
45578             this.uploadProgress();
45579
45580         }else if (o.clientValidation !== false){ // client validation failed
45581             this.failureType = Roo.form.Action.CLIENT_INVALID;
45582             this.form.afterAction(this, false);
45583         }
45584     },
45585
45586     success : function(response)
45587     {
45588         this.uploadComplete= true;
45589         if (this.haveProgress) {
45590             Roo.MessageBox.hide();
45591         }
45592         
45593         
45594         var result = this.processResponse(response);
45595         if(result === true || result.success){
45596             this.form.afterAction(this, true);
45597             return;
45598         }
45599         if(result.errors){
45600             this.form.markInvalid(result.errors);
45601             this.failureType = Roo.form.Action.SERVER_INVALID;
45602         }
45603         this.form.afterAction(this, false);
45604     },
45605     failure : function(response)
45606     {
45607         this.uploadComplete= true;
45608         if (this.haveProgress) {
45609             Roo.MessageBox.hide();
45610         }
45611         
45612         this.response = response;
45613         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45614         this.form.afterAction(this, false);
45615     },
45616     
45617     handleResponse : function(response){
45618         if(this.form.errorReader){
45619             var rs = this.form.errorReader.read(response);
45620             var errors = [];
45621             if(rs.records){
45622                 for(var i = 0, len = rs.records.length; i < len; i++) {
45623                     var r = rs.records[i];
45624                     errors[i] = r.data;
45625                 }
45626             }
45627             if(errors.length < 1){
45628                 errors = null;
45629             }
45630             return {
45631                 success : rs.success,
45632                 errors : errors
45633             };
45634         }
45635         var ret = false;
45636         try {
45637             ret = Roo.decode(response.responseText);
45638         } catch (e) {
45639             ret = {
45640                 success: false,
45641                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45642                 errors : []
45643             };
45644         }
45645         return ret;
45646         
45647     }
45648 });
45649
45650
45651 Roo.form.Action.Load = function(form, options){
45652     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45653     this.reader = this.form.reader;
45654 };
45655
45656 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45657     type : 'load',
45658
45659     run : function(){
45660         
45661         Roo.Ajax.request(Roo.apply(
45662                 this.createCallback(), {
45663                     method:this.getMethod(),
45664                     url:this.getUrl(false),
45665                     params:this.getParams()
45666         }));
45667     },
45668
45669     success : function(response){
45670         
45671         var result = this.processResponse(response);
45672         if(result === true || !result.success || !result.data){
45673             this.failureType = Roo.form.Action.LOAD_FAILURE;
45674             this.form.afterAction(this, false);
45675             return;
45676         }
45677         this.form.clearInvalid();
45678         this.form.setValues(result.data);
45679         this.form.afterAction(this, true);
45680     },
45681
45682     handleResponse : function(response){
45683         if(this.form.reader){
45684             var rs = this.form.reader.read(response);
45685             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45686             return {
45687                 success : rs.success,
45688                 data : data
45689             };
45690         }
45691         return Roo.decode(response.responseText);
45692     }
45693 });
45694
45695 Roo.form.Action.ACTION_TYPES = {
45696     'load' : Roo.form.Action.Load,
45697     'submit' : Roo.form.Action.Submit
45698 };/*
45699  * Based on:
45700  * Ext JS Library 1.1.1
45701  * Copyright(c) 2006-2007, Ext JS, LLC.
45702  *
45703  * Originally Released Under LGPL - original licence link has changed is not relivant.
45704  *
45705  * Fork - LGPL
45706  * <script type="text/javascript">
45707  */
45708  
45709 /**
45710  * @class Roo.form.Layout
45711  * @extends Roo.Component
45712  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45713  * @constructor
45714  * @param {Object} config Configuration options
45715  */
45716 Roo.form.Layout = function(config){
45717     var xitems = [];
45718     if (config.items) {
45719         xitems = config.items;
45720         delete config.items;
45721     }
45722     Roo.form.Layout.superclass.constructor.call(this, config);
45723     this.stack = [];
45724     Roo.each(xitems, this.addxtype, this);
45725      
45726 };
45727
45728 Roo.extend(Roo.form.Layout, Roo.Component, {
45729     /**
45730      * @cfg {String/Object} autoCreate
45731      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45732      */
45733     /**
45734      * @cfg {String/Object/Function} style
45735      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45736      * a function which returns such a specification.
45737      */
45738     /**
45739      * @cfg {String} labelAlign
45740      * Valid values are "left," "top" and "right" (defaults to "left")
45741      */
45742     /**
45743      * @cfg {Number} labelWidth
45744      * Fixed width in pixels of all field labels (defaults to undefined)
45745      */
45746     /**
45747      * @cfg {Boolean} clear
45748      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45749      */
45750     clear : true,
45751     /**
45752      * @cfg {String} labelSeparator
45753      * The separator to use after field labels (defaults to ':')
45754      */
45755     labelSeparator : ':',
45756     /**
45757      * @cfg {Boolean} hideLabels
45758      * True to suppress the display of field labels in this layout (defaults to false)
45759      */
45760     hideLabels : false,
45761
45762     // private
45763     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45764     
45765     isLayout : true,
45766     
45767     // private
45768     onRender : function(ct, position){
45769         if(this.el){ // from markup
45770             this.el = Roo.get(this.el);
45771         }else {  // generate
45772             var cfg = this.getAutoCreate();
45773             this.el = ct.createChild(cfg, position);
45774         }
45775         if(this.style){
45776             this.el.applyStyles(this.style);
45777         }
45778         if(this.labelAlign){
45779             this.el.addClass('x-form-label-'+this.labelAlign);
45780         }
45781         if(this.hideLabels){
45782             this.labelStyle = "display:none";
45783             this.elementStyle = "padding-left:0;";
45784         }else{
45785             if(typeof this.labelWidth == 'number'){
45786                 this.labelStyle = "width:"+this.labelWidth+"px;";
45787                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45788             }
45789             if(this.labelAlign == 'top'){
45790                 this.labelStyle = "width:auto;";
45791                 this.elementStyle = "padding-left:0;";
45792             }
45793         }
45794         var stack = this.stack;
45795         var slen = stack.length;
45796         if(slen > 0){
45797             if(!this.fieldTpl){
45798                 var t = new Roo.Template(
45799                     '<div class="x-form-item {5}">',
45800                         '<label for="{0}" style="{2}">{1}{4}</label>',
45801                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45802                         '</div>',
45803                     '</div><div class="x-form-clear-left"></div>'
45804                 );
45805                 t.disableFormats = true;
45806                 t.compile();
45807                 Roo.form.Layout.prototype.fieldTpl = t;
45808             }
45809             for(var i = 0; i < slen; i++) {
45810                 if(stack[i].isFormField){
45811                     this.renderField(stack[i]);
45812                 }else{
45813                     this.renderComponent(stack[i]);
45814                 }
45815             }
45816         }
45817         if(this.clear){
45818             this.el.createChild({cls:'x-form-clear'});
45819         }
45820     },
45821
45822     // private
45823     renderField : function(f){
45824         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45825                f.id, //0
45826                f.fieldLabel, //1
45827                f.labelStyle||this.labelStyle||'', //2
45828                this.elementStyle||'', //3
45829                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45830                f.itemCls||this.itemCls||''  //5
45831        ], true).getPrevSibling());
45832     },
45833
45834     // private
45835     renderComponent : function(c){
45836         c.render(c.isLayout ? this.el : this.el.createChild());    
45837     },
45838     /**
45839      * Adds a object form elements (using the xtype property as the factory method.)
45840      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45841      * @param {Object} config 
45842      */
45843     addxtype : function(o)
45844     {
45845         // create the lement.
45846         o.form = this.form;
45847         var fe = Roo.factory(o, Roo.form);
45848         this.form.allItems.push(fe);
45849         this.stack.push(fe);
45850         
45851         if (fe.isFormField) {
45852             this.form.items.add(fe);
45853         }
45854          
45855         return fe;
45856     }
45857 });
45858
45859 /**
45860  * @class Roo.form.Column
45861  * @extends Roo.form.Layout
45862  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45863  * @constructor
45864  * @param {Object} config Configuration options
45865  */
45866 Roo.form.Column = function(config){
45867     Roo.form.Column.superclass.constructor.call(this, config);
45868 };
45869
45870 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45871     /**
45872      * @cfg {Number/String} width
45873      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45874      */
45875     /**
45876      * @cfg {String/Object} autoCreate
45877      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45878      */
45879
45880     // private
45881     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45882
45883     // private
45884     onRender : function(ct, position){
45885         Roo.form.Column.superclass.onRender.call(this, ct, position);
45886         if(this.width){
45887             this.el.setWidth(this.width);
45888         }
45889     }
45890 });
45891
45892
45893 /**
45894  * @class Roo.form.Row
45895  * @extends Roo.form.Layout
45896  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45897  * @constructor
45898  * @param {Object} config Configuration options
45899  */
45900
45901  
45902 Roo.form.Row = function(config){
45903     Roo.form.Row.superclass.constructor.call(this, config);
45904 };
45905  
45906 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45907       /**
45908      * @cfg {Number/String} width
45909      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45910      */
45911     /**
45912      * @cfg {Number/String} height
45913      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45914      */
45915     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45916     
45917     padWidth : 20,
45918     // private
45919     onRender : function(ct, position){
45920         //console.log('row render');
45921         if(!this.rowTpl){
45922             var t = new Roo.Template(
45923                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45924                     '<label for="{0}" style="{2}">{1}{4}</label>',
45925                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45926                     '</div>',
45927                 '</div>'
45928             );
45929             t.disableFormats = true;
45930             t.compile();
45931             Roo.form.Layout.prototype.rowTpl = t;
45932         }
45933         this.fieldTpl = this.rowTpl;
45934         
45935         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45936         var labelWidth = 100;
45937         
45938         if ((this.labelAlign != 'top')) {
45939             if (typeof this.labelWidth == 'number') {
45940                 labelWidth = this.labelWidth
45941             }
45942             this.padWidth =  20 + labelWidth;
45943             
45944         }
45945         
45946         Roo.form.Column.superclass.onRender.call(this, ct, position);
45947         if(this.width){
45948             this.el.setWidth(this.width);
45949         }
45950         if(this.height){
45951             this.el.setHeight(this.height);
45952         }
45953     },
45954     
45955     // private
45956     renderField : function(f){
45957         f.fieldEl = this.fieldTpl.append(this.el, [
45958                f.id, f.fieldLabel,
45959                f.labelStyle||this.labelStyle||'',
45960                this.elementStyle||'',
45961                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45962                f.itemCls||this.itemCls||'',
45963                f.width ? f.width + this.padWidth : 160 + this.padWidth
45964        ],true);
45965     }
45966 });
45967  
45968
45969 /**
45970  * @class Roo.form.FieldSet
45971  * @extends Roo.form.Layout
45972  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45973  * @constructor
45974  * @param {Object} config Configuration options
45975  */
45976 Roo.form.FieldSet = function(config){
45977     Roo.form.FieldSet.superclass.constructor.call(this, config);
45978 };
45979
45980 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45981     /**
45982      * @cfg {String} legend
45983      * The text to display as the legend for the FieldSet (defaults to '')
45984      */
45985     /**
45986      * @cfg {String/Object} autoCreate
45987      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45988      */
45989
45990     // private
45991     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45992
45993     // private
45994     onRender : function(ct, position){
45995         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45996         if(this.legend){
45997             this.setLegend(this.legend);
45998         }
45999     },
46000
46001     // private
46002     setLegend : function(text){
46003         if(this.rendered){
46004             this.el.child('legend').update(text);
46005         }
46006     }
46007 });/*
46008  * Based on:
46009  * Ext JS Library 1.1.1
46010  * Copyright(c) 2006-2007, Ext JS, LLC.
46011  *
46012  * Originally Released Under LGPL - original licence link has changed is not relivant.
46013  *
46014  * Fork - LGPL
46015  * <script type="text/javascript">
46016  */
46017 /**
46018  * @class Roo.form.VTypes
46019  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46020  * @singleton
46021  */
46022 Roo.form.VTypes = function(){
46023     // closure these in so they are only created once.
46024     var alpha = /^[a-zA-Z_]+$/;
46025     var alphanum = /^[a-zA-Z0-9_]+$/;
46026     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46027     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46028
46029     // All these messages and functions are configurable
46030     return {
46031         /**
46032          * The function used to validate email addresses
46033          * @param {String} value The email address
46034          */
46035         'email' : function(v){
46036             return email.test(v);
46037         },
46038         /**
46039          * The error text to display when the email validation function returns false
46040          * @type String
46041          */
46042         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46043         /**
46044          * The keystroke filter mask to be applied on email input
46045          * @type RegExp
46046          */
46047         'emailMask' : /[a-z0-9_\.\-@]/i,
46048
46049         /**
46050          * The function used to validate URLs
46051          * @param {String} value The URL
46052          */
46053         'url' : function(v){
46054             return url.test(v);
46055         },
46056         /**
46057          * The error text to display when the url validation function returns false
46058          * @type String
46059          */
46060         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46061         
46062         /**
46063          * The function used to validate alpha values
46064          * @param {String} value The value
46065          */
46066         'alpha' : function(v){
46067             return alpha.test(v);
46068         },
46069         /**
46070          * The error text to display when the alpha validation function returns false
46071          * @type String
46072          */
46073         'alphaText' : 'This field should only contain letters and _',
46074         /**
46075          * The keystroke filter mask to be applied on alpha input
46076          * @type RegExp
46077          */
46078         'alphaMask' : /[a-z_]/i,
46079
46080         /**
46081          * The function used to validate alphanumeric values
46082          * @param {String} value The value
46083          */
46084         'alphanum' : function(v){
46085             return alphanum.test(v);
46086         },
46087         /**
46088          * The error text to display when the alphanumeric validation function returns false
46089          * @type String
46090          */
46091         'alphanumText' : 'This field should only contain letters, numbers and _',
46092         /**
46093          * The keystroke filter mask to be applied on alphanumeric input
46094          * @type RegExp
46095          */
46096         'alphanumMask' : /[a-z0-9_]/i
46097     };
46098 }();//<script type="text/javascript">
46099
46100 /**
46101  * @class Roo.form.FCKeditor
46102  * @extends Roo.form.TextArea
46103  * Wrapper around the FCKEditor http://www.fckeditor.net
46104  * @constructor
46105  * Creates a new FCKeditor
46106  * @param {Object} config Configuration options
46107  */
46108 Roo.form.FCKeditor = function(config){
46109     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46110     this.addEvents({
46111          /**
46112          * @event editorinit
46113          * Fired when the editor is initialized - you can add extra handlers here..
46114          * @param {FCKeditor} this
46115          * @param {Object} the FCK object.
46116          */
46117         editorinit : true
46118     });
46119     
46120     
46121 };
46122 Roo.form.FCKeditor.editors = { };
46123 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46124 {
46125     //defaultAutoCreate : {
46126     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46127     //},
46128     // private
46129     /**
46130      * @cfg {Object} fck options - see fck manual for details.
46131      */
46132     fckconfig : false,
46133     
46134     /**
46135      * @cfg {Object} fck toolbar set (Basic or Default)
46136      */
46137     toolbarSet : 'Basic',
46138     /**
46139      * @cfg {Object} fck BasePath
46140      */ 
46141     basePath : '/fckeditor/',
46142     
46143     
46144     frame : false,
46145     
46146     value : '',
46147     
46148    
46149     onRender : function(ct, position)
46150     {
46151         if(!this.el){
46152             this.defaultAutoCreate = {
46153                 tag: "textarea",
46154                 style:"width:300px;height:60px;",
46155                 autocomplete: "off"
46156             };
46157         }
46158         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46159         /*
46160         if(this.grow){
46161             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46162             if(this.preventScrollbars){
46163                 this.el.setStyle("overflow", "hidden");
46164             }
46165             this.el.setHeight(this.growMin);
46166         }
46167         */
46168         //console.log('onrender' + this.getId() );
46169         Roo.form.FCKeditor.editors[this.getId()] = this;
46170          
46171
46172         this.replaceTextarea() ;
46173         
46174     },
46175     
46176     getEditor : function() {
46177         return this.fckEditor;
46178     },
46179     /**
46180      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46181      * @param {Mixed} value The value to set
46182      */
46183     
46184     
46185     setValue : function(value)
46186     {
46187         //console.log('setValue: ' + value);
46188         
46189         if(typeof(value) == 'undefined') { // not sure why this is happending...
46190             return;
46191         }
46192         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46193         
46194         //if(!this.el || !this.getEditor()) {
46195         //    this.value = value;
46196             //this.setValue.defer(100,this,[value]);    
46197         //    return;
46198         //} 
46199         
46200         if(!this.getEditor()) {
46201             return;
46202         }
46203         
46204         this.getEditor().SetData(value);
46205         
46206         //
46207
46208     },
46209
46210     /**
46211      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46212      * @return {Mixed} value The field value
46213      */
46214     getValue : function()
46215     {
46216         
46217         if (this.frame && this.frame.dom.style.display == 'none') {
46218             return Roo.form.FCKeditor.superclass.getValue.call(this);
46219         }
46220         
46221         if(!this.el || !this.getEditor()) {
46222            
46223            // this.getValue.defer(100,this); 
46224             return this.value;
46225         }
46226        
46227         
46228         var value=this.getEditor().GetData();
46229         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46230         return Roo.form.FCKeditor.superclass.getValue.call(this);
46231         
46232
46233     },
46234
46235     /**
46236      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46237      * @return {Mixed} value The field value
46238      */
46239     getRawValue : function()
46240     {
46241         if (this.frame && this.frame.dom.style.display == 'none') {
46242             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46243         }
46244         
46245         if(!this.el || !this.getEditor()) {
46246             //this.getRawValue.defer(100,this); 
46247             return this.value;
46248             return;
46249         }
46250         
46251         
46252         
46253         var value=this.getEditor().GetData();
46254         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46255         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46256          
46257     },
46258     
46259     setSize : function(w,h) {
46260         
46261         
46262         
46263         //if (this.frame && this.frame.dom.style.display == 'none') {
46264         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46265         //    return;
46266         //}
46267         //if(!this.el || !this.getEditor()) {
46268         //    this.setSize.defer(100,this, [w,h]); 
46269         //    return;
46270         //}
46271         
46272         
46273         
46274         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46275         
46276         this.frame.dom.setAttribute('width', w);
46277         this.frame.dom.setAttribute('height', h);
46278         this.frame.setSize(w,h);
46279         
46280     },
46281     
46282     toggleSourceEdit : function(value) {
46283         
46284       
46285          
46286         this.el.dom.style.display = value ? '' : 'none';
46287         this.frame.dom.style.display = value ?  'none' : '';
46288         
46289     },
46290     
46291     
46292     focus: function(tag)
46293     {
46294         if (this.frame.dom.style.display == 'none') {
46295             return Roo.form.FCKeditor.superclass.focus.call(this);
46296         }
46297         if(!this.el || !this.getEditor()) {
46298             this.focus.defer(100,this, [tag]); 
46299             return;
46300         }
46301         
46302         
46303         
46304         
46305         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46306         this.getEditor().Focus();
46307         if (tgs.length) {
46308             if (!this.getEditor().Selection.GetSelection()) {
46309                 this.focus.defer(100,this, [tag]); 
46310                 return;
46311             }
46312             
46313             
46314             var r = this.getEditor().EditorDocument.createRange();
46315             r.setStart(tgs[0],0);
46316             r.setEnd(tgs[0],0);
46317             this.getEditor().Selection.GetSelection().removeAllRanges();
46318             this.getEditor().Selection.GetSelection().addRange(r);
46319             this.getEditor().Focus();
46320         }
46321         
46322     },
46323     
46324     
46325     
46326     replaceTextarea : function()
46327     {
46328         if ( document.getElementById( this.getId() + '___Frame' ) )
46329             return ;
46330         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46331         //{
46332             // We must check the elements firstly using the Id and then the name.
46333         var oTextarea = document.getElementById( this.getId() );
46334         
46335         var colElementsByName = document.getElementsByName( this.getId() ) ;
46336          
46337         oTextarea.style.display = 'none' ;
46338
46339         if ( oTextarea.tabIndex ) {            
46340             this.TabIndex = oTextarea.tabIndex ;
46341         }
46342         
46343         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46344         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46345         this.frame = Roo.get(this.getId() + '___Frame')
46346     },
46347     
46348     _getConfigHtml : function()
46349     {
46350         var sConfig = '' ;
46351
46352         for ( var o in this.fckconfig ) {
46353             sConfig += sConfig.length > 0  ? '&amp;' : '';
46354             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46355         }
46356
46357         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46358     },
46359     
46360     
46361     _getIFrameHtml : function()
46362     {
46363         var sFile = 'fckeditor.html' ;
46364         /* no idea what this is about..
46365         try
46366         {
46367             if ( (/fcksource=true/i).test( window.top.location.search ) )
46368                 sFile = 'fckeditor.original.html' ;
46369         }
46370         catch (e) { 
46371         */
46372
46373         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46374         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46375         
46376         
46377         var html = '<iframe id="' + this.getId() +
46378             '___Frame" src="' + sLink +
46379             '" width="' + this.width +
46380             '" height="' + this.height + '"' +
46381             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46382             ' frameborder="0" scrolling="no"></iframe>' ;
46383
46384         return html ;
46385     },
46386     
46387     _insertHtmlBefore : function( html, element )
46388     {
46389         if ( element.insertAdjacentHTML )       {
46390             // IE
46391             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46392         } else { // Gecko
46393             var oRange = document.createRange() ;
46394             oRange.setStartBefore( element ) ;
46395             var oFragment = oRange.createContextualFragment( html );
46396             element.parentNode.insertBefore( oFragment, element ) ;
46397         }
46398     }
46399     
46400     
46401   
46402     
46403     
46404     
46405     
46406
46407 });
46408
46409 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46410
46411 function FCKeditor_OnComplete(editorInstance){
46412     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46413     f.fckEditor = editorInstance;
46414     //console.log("loaded");
46415     f.fireEvent('editorinit', f, editorInstance);
46416
46417   
46418
46419  
46420
46421
46422
46423
46424
46425
46426
46427
46428
46429
46430
46431
46432
46433
46434
46435 //<script type="text/javascript">
46436 /**
46437  * @class Roo.form.GridField
46438  * @extends Roo.form.Field
46439  * Embed a grid (or editable grid into a form)
46440  * STATUS ALPHA
46441  * 
46442  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46443  * it needs 
46444  * xgrid.store = Roo.data.Store
46445  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46446  * xgrid.store.reader = Roo.data.JsonReader 
46447  * 
46448  * 
46449  * @constructor
46450  * Creates a new GridField
46451  * @param {Object} config Configuration options
46452  */
46453 Roo.form.GridField = function(config){
46454     Roo.form.GridField.superclass.constructor.call(this, config);
46455      
46456 };
46457
46458 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46459     /**
46460      * @cfg {Number} width  - used to restrict width of grid..
46461      */
46462     width : 100,
46463     /**
46464      * @cfg {Number} height - used to restrict height of grid..
46465      */
46466     height : 50,
46467      /**
46468      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46469          * 
46470          *}
46471      */
46472     xgrid : false, 
46473     /**
46474      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46475      * {tag: "input", type: "checkbox", autocomplete: "off"})
46476      */
46477    // defaultAutoCreate : { tag: 'div' },
46478     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46479     /**
46480      * @cfg {String} addTitle Text to include for adding a title.
46481      */
46482     addTitle : false,
46483     //
46484     onResize : function(){
46485         Roo.form.Field.superclass.onResize.apply(this, arguments);
46486     },
46487
46488     initEvents : function(){
46489         // Roo.form.Checkbox.superclass.initEvents.call(this);
46490         // has no events...
46491        
46492     },
46493
46494
46495     getResizeEl : function(){
46496         return this.wrap;
46497     },
46498
46499     getPositionEl : function(){
46500         return this.wrap;
46501     },
46502
46503     // private
46504     onRender : function(ct, position){
46505         
46506         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46507         var style = this.style;
46508         delete this.style;
46509         
46510         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46511         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46512         this.viewEl = this.wrap.createChild({ tag: 'div' });
46513         if (style) {
46514             this.viewEl.applyStyles(style);
46515         }
46516         if (this.width) {
46517             this.viewEl.setWidth(this.width);
46518         }
46519         if (this.height) {
46520             this.viewEl.setHeight(this.height);
46521         }
46522         //if(this.inputValue !== undefined){
46523         //this.setValue(this.value);
46524         
46525         
46526         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46527         
46528         
46529         this.grid.render();
46530         this.grid.getDataSource().on('remove', this.refreshValue, this);
46531         this.grid.getDataSource().on('update', this.refreshValue, this);
46532         this.grid.on('afteredit', this.refreshValue, this);
46533  
46534     },
46535      
46536     
46537     /**
46538      * Sets the value of the item. 
46539      * @param {String} either an object  or a string..
46540      */
46541     setValue : function(v){
46542         //this.value = v;
46543         v = v || []; // empty set..
46544         // this does not seem smart - it really only affects memoryproxy grids..
46545         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46546             var ds = this.grid.getDataSource();
46547             // assumes a json reader..
46548             var data = {}
46549             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46550             ds.loadData( data);
46551         }
46552         // clear selection so it does not get stale.
46553         if (this.grid.sm) { 
46554             this.grid.sm.clearSelections();
46555         }
46556         
46557         Roo.form.GridField.superclass.setValue.call(this, v);
46558         this.refreshValue();
46559         // should load data in the grid really....
46560     },
46561     
46562     // private
46563     refreshValue: function() {
46564          var val = [];
46565         this.grid.getDataSource().each(function(r) {
46566             val.push(r.data);
46567         });
46568         this.el.dom.value = Roo.encode(val);
46569     }
46570     
46571      
46572     
46573     
46574 });/*
46575  * Based on:
46576  * Ext JS Library 1.1.1
46577  * Copyright(c) 2006-2007, Ext JS, LLC.
46578  *
46579  * Originally Released Under LGPL - original licence link has changed is not relivant.
46580  *
46581  * Fork - LGPL
46582  * <script type="text/javascript">
46583  */
46584 /**
46585  * @class Roo.form.DisplayField
46586  * @extends Roo.form.Field
46587  * A generic Field to display non-editable data.
46588  * @constructor
46589  * Creates a new Display Field item.
46590  * @param {Object} config Configuration options
46591  */
46592 Roo.form.DisplayField = function(config){
46593     Roo.form.DisplayField.superclass.constructor.call(this, config);
46594     
46595 };
46596
46597 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46598     inputType:      'hidden',
46599     allowBlank:     true,
46600     readOnly:         true,
46601     
46602  
46603     /**
46604      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46605      */
46606     focusClass : undefined,
46607     /**
46608      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46609      */
46610     fieldClass: 'x-form-field',
46611     
46612      /**
46613      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46614      */
46615     valueRenderer: undefined,
46616     
46617     width: 100,
46618     /**
46619      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46620      * {tag: "input", type: "checkbox", autocomplete: "off"})
46621      */
46622      
46623  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46624
46625     onResize : function(){
46626         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46627         
46628     },
46629
46630     initEvents : function(){
46631         // Roo.form.Checkbox.superclass.initEvents.call(this);
46632         // has no events...
46633        
46634     },
46635
46636
46637     getResizeEl : function(){
46638         return this.wrap;
46639     },
46640
46641     getPositionEl : function(){
46642         return this.wrap;
46643     },
46644
46645     // private
46646     onRender : function(ct, position){
46647         
46648         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46649         //if(this.inputValue !== undefined){
46650         this.wrap = this.el.wrap();
46651         
46652         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46653         
46654         if (this.bodyStyle) {
46655             this.viewEl.applyStyles(this.bodyStyle);
46656         }
46657         //this.viewEl.setStyle('padding', '2px');
46658         
46659         this.setValue(this.value);
46660         
46661     },
46662 /*
46663     // private
46664     initValue : Roo.emptyFn,
46665
46666   */
46667
46668         // private
46669     onClick : function(){
46670         
46671     },
46672
46673     /**
46674      * Sets the checked state of the checkbox.
46675      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46676      */
46677     setValue : function(v){
46678         this.value = v;
46679         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46680         // this might be called before we have a dom element..
46681         if (!this.viewEl) {
46682             return;
46683         }
46684         this.viewEl.dom.innerHTML = html;
46685         Roo.form.DisplayField.superclass.setValue.call(this, v);
46686
46687     }
46688 });/*
46689  * 
46690  * Licence- LGPL
46691  * 
46692  */
46693
46694 /**
46695  * @class Roo.form.DayPicker
46696  * @extends Roo.form.Field
46697  * A Day picker show [M] [T] [W] ....
46698  * @constructor
46699  * Creates a new Day Picker
46700  * @param {Object} config Configuration options
46701  */
46702 Roo.form.DayPicker= function(config){
46703     Roo.form.DayPicker.superclass.constructor.call(this, config);
46704      
46705 };
46706
46707 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46708     /**
46709      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46710      */
46711     focusClass : undefined,
46712     /**
46713      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46714      */
46715     fieldClass: "x-form-field",
46716    
46717     /**
46718      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46719      * {tag: "input", type: "checkbox", autocomplete: "off"})
46720      */
46721     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46722     
46723    
46724     actionMode : 'viewEl', 
46725     //
46726     // private
46727  
46728     inputType : 'hidden',
46729     
46730      
46731     inputElement: false, // real input element?
46732     basedOn: false, // ????
46733     
46734     isFormField: true, // not sure where this is needed!!!!
46735
46736     onResize : function(){
46737         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46738         if(!this.boxLabel){
46739             this.el.alignTo(this.wrap, 'c-c');
46740         }
46741     },
46742
46743     initEvents : function(){
46744         Roo.form.Checkbox.superclass.initEvents.call(this);
46745         this.el.on("click", this.onClick,  this);
46746         this.el.on("change", this.onClick,  this);
46747     },
46748
46749
46750     getResizeEl : function(){
46751         return this.wrap;
46752     },
46753
46754     getPositionEl : function(){
46755         return this.wrap;
46756     },
46757
46758     
46759     // private
46760     onRender : function(ct, position){
46761         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46762        
46763         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46764         
46765         var r1 = '<table><tr>';
46766         var r2 = '<tr class="x-form-daypick-icons">';
46767         for (var i=0; i < 7; i++) {
46768             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46769             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46770         }
46771         
46772         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46773         viewEl.select('img').on('click', this.onClick, this);
46774         this.viewEl = viewEl;   
46775         
46776         
46777         // this will not work on Chrome!!!
46778         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46779         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46780         
46781         
46782           
46783
46784     },
46785
46786     // private
46787     initValue : Roo.emptyFn,
46788
46789     /**
46790      * Returns the checked state of the checkbox.
46791      * @return {Boolean} True if checked, else false
46792      */
46793     getValue : function(){
46794         return this.el.dom.value;
46795         
46796     },
46797
46798         // private
46799     onClick : function(e){ 
46800         //this.setChecked(!this.checked);
46801         Roo.get(e.target).toggleClass('x-menu-item-checked');
46802         this.refreshValue();
46803         //if(this.el.dom.checked != this.checked){
46804         //    this.setValue(this.el.dom.checked);
46805        // }
46806     },
46807     
46808     // private
46809     refreshValue : function()
46810     {
46811         var val = '';
46812         this.viewEl.select('img',true).each(function(e,i,n)  {
46813             val += e.is(".x-menu-item-checked") ? String(n) : '';
46814         });
46815         this.setValue(val, true);
46816     },
46817
46818     /**
46819      * Sets the checked state of the checkbox.
46820      * On is always based on a string comparison between inputValue and the param.
46821      * @param {Boolean/String} value - the value to set 
46822      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46823      */
46824     setValue : function(v,suppressEvent){
46825         if (!this.el.dom) {
46826             return;
46827         }
46828         var old = this.el.dom.value ;
46829         this.el.dom.value = v;
46830         if (suppressEvent) {
46831             return ;
46832         }
46833          
46834         // update display..
46835         this.viewEl.select('img',true).each(function(e,i,n)  {
46836             
46837             var on = e.is(".x-menu-item-checked");
46838             var newv = v.indexOf(String(n)) > -1;
46839             if (on != newv) {
46840                 e.toggleClass('x-menu-item-checked');
46841             }
46842             
46843         });
46844         
46845         
46846         this.fireEvent('change', this, v, old);
46847         
46848         
46849     },
46850    
46851     // handle setting of hidden value by some other method!!?!?
46852     setFromHidden: function()
46853     {
46854         if(!this.el){
46855             return;
46856         }
46857         //console.log("SET FROM HIDDEN");
46858         //alert('setFrom hidden');
46859         this.setValue(this.el.dom.value);
46860     },
46861     
46862     onDestroy : function()
46863     {
46864         if(this.viewEl){
46865             Roo.get(this.viewEl).remove();
46866         }
46867          
46868         Roo.form.DayPicker.superclass.onDestroy.call(this);
46869     }
46870
46871 });/*
46872  * RooJS Library 1.1.1
46873  * Copyright(c) 2008-2011  Alan Knowles
46874  *
46875  * License - LGPL
46876  */
46877  
46878
46879 /**
46880  * @class Roo.form.ComboCheck
46881  * @extends Roo.form.ComboBox
46882  * A combobox for multiple select items.
46883  *
46884  * FIXME - could do with a reset button..
46885  * 
46886  * @constructor
46887  * Create a new ComboCheck
46888  * @param {Object} config Configuration options
46889  */
46890 Roo.form.ComboCheck = function(config){
46891     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46892     // should verify some data...
46893     // like
46894     // hiddenName = required..
46895     // displayField = required
46896     // valudField == required
46897     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46898     var _t = this;
46899     Roo.each(req, function(e) {
46900         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46901             throw "Roo.form.ComboCheck : missing value for: " + e;
46902         }
46903     });
46904     
46905     
46906 };
46907
46908 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46909      
46910      
46911     editable : false,
46912      
46913     selectedClass: 'x-menu-item-checked', 
46914     
46915     // private
46916     onRender : function(ct, position){
46917         var _t = this;
46918         
46919         
46920         
46921         if(!this.tpl){
46922             var cls = 'x-combo-list';
46923
46924             
46925             this.tpl =  new Roo.Template({
46926                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46927                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46928                    '<span>{' + this.displayField + '}</span>' +
46929                     '</div>' 
46930                 
46931             });
46932         }
46933  
46934         
46935         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46936         this.view.singleSelect = false;
46937         this.view.multiSelect = true;
46938         this.view.toggleSelect = true;
46939         this.pageTb.add(new Roo.Toolbar.Fill(), {
46940             
46941             text: 'Done',
46942             handler: function()
46943             {
46944                 _t.collapse();
46945             }
46946         });
46947     },
46948     
46949     onViewOver : function(e, t){
46950         // do nothing...
46951         return;
46952         
46953     },
46954     
46955     onViewClick : function(doFocus,index){
46956         return;
46957         
46958     },
46959     select: function () {
46960         //Roo.log("SELECT CALLED");
46961     },
46962      
46963     selectByValue : function(xv, scrollIntoView){
46964         var ar = this.getValueArray();
46965         var sels = [];
46966         
46967         Roo.each(ar, function(v) {
46968             if(v === undefined || v === null){
46969                 return;
46970             }
46971             var r = this.findRecord(this.valueField, v);
46972             if(r){
46973                 sels.push(this.store.indexOf(r))
46974                 
46975             }
46976         },this);
46977         this.view.select(sels);
46978         return false;
46979     },
46980     
46981     
46982     
46983     onSelect : function(record, index){
46984        // Roo.log("onselect Called");
46985        // this is only called by the clear button now..
46986         this.view.clearSelections();
46987         this.setValue('[]');
46988         if (this.value != this.valueBefore) {
46989             this.fireEvent('change', this, this.value, this.valueBefore);
46990             this.valueBefore = this.value;
46991         }
46992     },
46993     getValueArray : function()
46994     {
46995         var ar = [] ;
46996         
46997         try {
46998             //Roo.log(this.value);
46999             if (typeof(this.value) == 'undefined') {
47000                 return [];
47001             }
47002             var ar = Roo.decode(this.value);
47003             return  ar instanceof Array ? ar : []; //?? valid?
47004             
47005         } catch(e) {
47006             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47007             return [];
47008         }
47009          
47010     },
47011     expand : function ()
47012     {
47013         
47014         Roo.form.ComboCheck.superclass.expand.call(this);
47015         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47016         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47017         
47018
47019     },
47020     
47021     collapse : function(){
47022         Roo.form.ComboCheck.superclass.collapse.call(this);
47023         var sl = this.view.getSelectedIndexes();
47024         var st = this.store;
47025         var nv = [];
47026         var tv = [];
47027         var r;
47028         Roo.each(sl, function(i) {
47029             r = st.getAt(i);
47030             nv.push(r.get(this.valueField));
47031         },this);
47032         this.setValue(Roo.encode(nv));
47033         if (this.value != this.valueBefore) {
47034
47035             this.fireEvent('change', this, this.value, this.valueBefore);
47036             this.valueBefore = this.value;
47037         }
47038         
47039     },
47040     
47041     setValue : function(v){
47042         // Roo.log(v);
47043         this.value = v;
47044         
47045         var vals = this.getValueArray();
47046         var tv = [];
47047         Roo.each(vals, function(k) {
47048             var r = this.findRecord(this.valueField, k);
47049             if(r){
47050                 tv.push(r.data[this.displayField]);
47051             }else if(this.valueNotFoundText !== undefined){
47052                 tv.push( this.valueNotFoundText );
47053             }
47054         },this);
47055        // Roo.log(tv);
47056         
47057         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47058         this.hiddenField.value = v;
47059         this.value = v;
47060     }
47061     
47062 });/*
47063  * Based on:
47064  * Ext JS Library 1.1.1
47065  * Copyright(c) 2006-2007, Ext JS, LLC.
47066  *
47067  * Originally Released Under LGPL - original licence link has changed is not relivant.
47068  *
47069  * Fork - LGPL
47070  * <script type="text/javascript">
47071  */
47072  
47073 /**
47074  * @class Roo.form.Signature
47075  * @extends Roo.form.Field
47076  * Signature field.  
47077  * @constructor
47078  * 
47079  * @param {Object} config Configuration options
47080  */
47081
47082 Roo.form.Signature = function(config){
47083     Roo.form.Signature.superclass.constructor.call(this, config);
47084     
47085     this.addEvents({// not in used??
47086          /**
47087          * @event confirm
47088          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47089              * @param {Roo.form.Signature} combo This combo box
47090              */
47091         'confirm' : true,
47092         /**
47093          * @event reset
47094          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47095              * @param {Roo.form.ComboBox} combo This combo box
47096              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47097              */
47098         'reset' : true
47099     });
47100 };
47101
47102 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47103     /**
47104      * @cfg {Object} labels Label to use when rendering a form.
47105      * defaults to 
47106      * labels : { 
47107      *      clear : "Clear",
47108      *      confirm : "Confirm"
47109      *  }
47110      */
47111     labels : { 
47112         clear : "Clear",
47113         confirm : "Confirm"
47114     },
47115     /**
47116      * @cfg {Number} width The signature panel width (defaults to 300)
47117      */
47118     width: 300,
47119     /**
47120      * @cfg {Number} height The signature panel height (defaults to 100)
47121      */
47122     height : 100,
47123     /**
47124      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47125      */
47126     allowBlank : false,
47127     
47128     //private
47129     // {Object} signPanel The signature SVG panel element (defaults to {})
47130     signPanel : {},
47131     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47132     isMouseDown : false,
47133     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47134     isConfirmed : false,
47135     // {String} signatureTmp SVG mapping string (defaults to empty string)
47136     signatureTmp : '',
47137     
47138     
47139     defaultAutoCreate : { // modified by initCompnoent..
47140         tag: "input",
47141         type:"hidden"
47142     },
47143
47144     // private
47145     onRender : function(ct, position){
47146         
47147         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47148         
47149         this.wrap = this.el.wrap({
47150             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47151         });
47152         
47153         this.createToolbar(this);
47154         this.signPanel = this.wrap.createChild({
47155                 tag: 'div',
47156                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47157             }, this.el
47158         );
47159             
47160         this.svgID = Roo.id();
47161         this.svgEl = this.signPanel.createChild({
47162               xmlns : 'http://www.w3.org/2000/svg',
47163               tag : 'svg',
47164               id : this.svgID + "-svg",
47165               width: this.width,
47166               height: this.height,
47167               viewBox: '0 0 '+this.width+' '+this.height,
47168               cn : [
47169                 {
47170                     tag: "rect",
47171                     id: this.svgID + "-svg-r",
47172                     width: this.width,
47173                     height: this.height,
47174                     fill: "#ffa"
47175                 },
47176                 {
47177                     tag: "line",
47178                     id: this.svgID + "-svg-l",
47179                     x1: "0", // start
47180                     y1: (this.height*0.8), // start set the line in 80% of height
47181                     x2: this.width, // end
47182                     y2: (this.height*0.8), // end set the line in 80% of height
47183                     'stroke': "#666",
47184                     'stroke-width': "1",
47185                     'stroke-dasharray': "3",
47186                     'shape-rendering': "crispEdges",
47187                     'pointer-events': "none"
47188                 },
47189                 {
47190                     tag: "path",
47191                     id: this.svgID + "-svg-p",
47192                     'stroke': "navy",
47193                     'stroke-width': "3",
47194                     'fill': "none",
47195                     'pointer-events': 'none'
47196                 }
47197               ]
47198         });
47199         this.createSVG();
47200         this.svgBox = this.svgEl.dom.getScreenCTM();
47201     },
47202     createSVG : function(){ 
47203         var svg = this.signPanel;
47204         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47205         var t = this;
47206
47207         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47208         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47209         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47210         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47211         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47212         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47213         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47214         
47215     },
47216     isTouchEvent : function(e){
47217         return e.type.match(/^touch/);
47218     },
47219     getCoords : function (e) {
47220         var pt    = this.svgEl.dom.createSVGPoint();
47221         pt.x = e.clientX; 
47222         pt.y = e.clientY;
47223         if (this.isTouchEvent(e)) {
47224             pt.x =  e.targetTouches[0].clientX 
47225             pt.y = e.targetTouches[0].clientY;
47226         }
47227         var a = this.svgEl.dom.getScreenCTM();
47228         var b = a.inverse();
47229         var mx = pt.matrixTransform(b);
47230         return mx.x + ',' + mx.y;
47231     },
47232     //mouse event headler 
47233     down : function (e) {
47234         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47235         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47236         
47237         this.isMouseDown = true;
47238         
47239         e.preventDefault();
47240     },
47241     move : function (e) {
47242         if (this.isMouseDown) {
47243             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47244             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47245         }
47246         
47247         e.preventDefault();
47248     },
47249     up : function (e) {
47250         this.isMouseDown = false;
47251         var sp = this.signatureTmp.split(' ');
47252         
47253         if(sp.length > 1){
47254             if(!sp[sp.length-2].match(/^L/)){
47255                 sp.pop();
47256                 sp.pop();
47257                 sp.push("");
47258                 this.signatureTmp = sp.join(" ");
47259             }
47260         }
47261         if(this.getValue() != this.signatureTmp){
47262             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47263             this.isConfirmed = false;
47264         }
47265         e.preventDefault();
47266     },
47267     
47268     /**
47269      * Protected method that will not generally be called directly. It
47270      * is called when the editor creates its toolbar. Override this method if you need to
47271      * add custom toolbar buttons.
47272      * @param {HtmlEditor} editor
47273      */
47274     createToolbar : function(editor){
47275          function btn(id, toggle, handler){
47276             var xid = fid + '-'+ id ;
47277             return {
47278                 id : xid,
47279                 cmd : id,
47280                 cls : 'x-btn-icon x-edit-'+id,
47281                 enableToggle:toggle !== false,
47282                 scope: editor, // was editor...
47283                 handler:handler||editor.relayBtnCmd,
47284                 clickEvent:'mousedown',
47285                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47286                 tabIndex:-1
47287             };
47288         }
47289         
47290         
47291         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47292         this.tb = tb;
47293         this.tb.add(
47294            {
47295                 cls : ' x-signature-btn x-signature-'+id,
47296                 scope: editor, // was editor...
47297                 handler: this.reset,
47298                 clickEvent:'mousedown',
47299                 text: this.labels.clear
47300             },
47301             {
47302                  xtype : 'Fill',
47303                  xns: Roo.Toolbar
47304             }, 
47305             {
47306                 cls : '  x-signature-btn x-signature-'+id,
47307                 scope: editor, // was editor...
47308                 handler: this.confirmHandler,
47309                 clickEvent:'mousedown',
47310                 text: this.labels.confirm
47311             }
47312         );
47313     
47314     },
47315     //public
47316     /**
47317      * when user is clicked confirm then show this image.....
47318      * 
47319      * @return {String} Image Data URI
47320      */
47321     getImageDataURI : function(){
47322         var svg = this.svgEl.dom.parentNode.innerHTML;
47323         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47324         return src; 
47325     },
47326     /**
47327      * 
47328      * @return {Boolean} this.isConfirmed
47329      */
47330     getConfirmed : function(){
47331         return this.isConfirmed;
47332     },
47333     /**
47334      * 
47335      * @return {Number} this.width
47336      */
47337     getWidth : function(){
47338         return this.width;
47339     },
47340     /**
47341      * 
47342      * @return {Number} this.height
47343      */
47344     getHeight : function(){
47345         return this.height;
47346     },
47347     // private
47348     getSignature : function(){
47349         return this.signatureTmp;
47350     },
47351     // private
47352     reset : function(){
47353         this.signatureTmp = '';
47354         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47355         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47356         this.isConfirmed = false;
47357         Roo.form.Signature.superclass.reset.call(this);
47358     },
47359     setSignature : function(s){
47360         this.signatureTmp = s;
47361         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47362         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47363         this.setValue(s);
47364         this.isConfirmed = false;
47365         Roo.form.Signature.superclass.reset.call(this);
47366     }, 
47367     test : function(){
47368 //        Roo.log(this.signPanel.dom.contentWindow.up())
47369     },
47370     //private
47371     setConfirmed : function(){
47372         
47373         
47374         
47375 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47376     },
47377     // private
47378     confirmHandler : function(){
47379         if(!this.getSignature()){
47380             return;
47381         }
47382         
47383         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47384         this.setValue(this.getSignature());
47385         this.isConfirmed = true;
47386         
47387         this.fireEvent('confirm', this);
47388     },
47389     // private
47390     // Subclasses should provide the validation implementation by overriding this
47391     validateValue : function(value){
47392         if(this.allowBlank){
47393             return true;
47394         }
47395         
47396         if(this.isConfirmed){
47397             return true;
47398         }
47399         return false;
47400     }
47401 });/*
47402  * Based on:
47403  * Ext JS Library 1.1.1
47404  * Copyright(c) 2006-2007, Ext JS, LLC.
47405  *
47406  * Originally Released Under LGPL - original licence link has changed is not relivant.
47407  *
47408  * Fork - LGPL
47409  * <script type="text/javascript">
47410  */
47411  
47412
47413 /**
47414  * @class Roo.form.ComboBox
47415  * @extends Roo.form.TriggerField
47416  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47417  * @constructor
47418  * Create a new ComboBox.
47419  * @param {Object} config Configuration options
47420  */
47421 Roo.form.Select = function(config){
47422     Roo.form.Select.superclass.constructor.call(this, config);
47423      
47424 };
47425
47426 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47427     /**
47428      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47429      */
47430     /**
47431      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47432      * rendering into an Roo.Editor, defaults to false)
47433      */
47434     /**
47435      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47436      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47437      */
47438     /**
47439      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47440      */
47441     /**
47442      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47443      * the dropdown list (defaults to undefined, with no header element)
47444      */
47445
47446      /**
47447      * @cfg {String/Roo.Template} tpl The template to use to render the output
47448      */
47449      
47450     // private
47451     defaultAutoCreate : {tag: "select"  },
47452     /**
47453      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47454      */
47455     listWidth: undefined,
47456     /**
47457      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47458      * mode = 'remote' or 'text' if mode = 'local')
47459      */
47460     displayField: undefined,
47461     /**
47462      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47463      * mode = 'remote' or 'value' if mode = 'local'). 
47464      * Note: use of a valueField requires the user make a selection
47465      * in order for a value to be mapped.
47466      */
47467     valueField: undefined,
47468     
47469     
47470     /**
47471      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47472      * field's data value (defaults to the underlying DOM element's name)
47473      */
47474     hiddenName: undefined,
47475     /**
47476      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47477      */
47478     listClass: '',
47479     /**
47480      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47481      */
47482     selectedClass: 'x-combo-selected',
47483     /**
47484      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47485      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47486      * which displays a downward arrow icon).
47487      */
47488     triggerClass : 'x-form-arrow-trigger',
47489     /**
47490      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47491      */
47492     shadow:'sides',
47493     /**
47494      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47495      * anchor positions (defaults to 'tl-bl')
47496      */
47497     listAlign: 'tl-bl?',
47498     /**
47499      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47500      */
47501     maxHeight: 300,
47502     /**
47503      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47504      * query specified by the allQuery config option (defaults to 'query')
47505      */
47506     triggerAction: 'query',
47507     /**
47508      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47509      * (defaults to 4, does not apply if editable = false)
47510      */
47511     minChars : 4,
47512     /**
47513      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47514      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47515      */
47516     typeAhead: false,
47517     /**
47518      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47519      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47520      */
47521     queryDelay: 500,
47522     /**
47523      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47524      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47525      */
47526     pageSize: 0,
47527     /**
47528      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47529      * when editable = true (defaults to false)
47530      */
47531     selectOnFocus:false,
47532     /**
47533      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47534      */
47535     queryParam: 'query',
47536     /**
47537      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47538      * when mode = 'remote' (defaults to 'Loading...')
47539      */
47540     loadingText: 'Loading...',
47541     /**
47542      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47543      */
47544     resizable: false,
47545     /**
47546      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47547      */
47548     handleHeight : 8,
47549     /**
47550      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47551      * traditional select (defaults to true)
47552      */
47553     editable: true,
47554     /**
47555      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47556      */
47557     allQuery: '',
47558     /**
47559      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47560      */
47561     mode: 'remote',
47562     /**
47563      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47564      * listWidth has a higher value)
47565      */
47566     minListWidth : 70,
47567     /**
47568      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47569      * allow the user to set arbitrary text into the field (defaults to false)
47570      */
47571     forceSelection:false,
47572     /**
47573      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47574      * if typeAhead = true (defaults to 250)
47575      */
47576     typeAheadDelay : 250,
47577     /**
47578      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47579      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47580      */
47581     valueNotFoundText : undefined,
47582     
47583     /**
47584      * @cfg {String} defaultValue The value displayed after loading the store.
47585      */
47586     defaultValue: '',
47587     
47588     /**
47589      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47590      */
47591     blockFocus : false,
47592     
47593     /**
47594      * @cfg {Boolean} disableClear Disable showing of clear button.
47595      */
47596     disableClear : false,
47597     /**
47598      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47599      */
47600     alwaysQuery : false,
47601     
47602     //private
47603     addicon : false,
47604     editicon: false,
47605     
47606     // element that contains real text value.. (when hidden is used..)
47607      
47608     // private
47609     onRender : function(ct, position){
47610         Roo.form.Field.prototype.onRender.call(this, ct, position);
47611         
47612         if(this.store){
47613             this.store.on('beforeload', this.onBeforeLoad, this);
47614             this.store.on('load', this.onLoad, this);
47615             this.store.on('loadexception', this.onLoadException, this);
47616             this.store.load({});
47617         }
47618         
47619         
47620         
47621     },
47622
47623     // private
47624     initEvents : function(){
47625         //Roo.form.ComboBox.superclass.initEvents.call(this);
47626  
47627     },
47628
47629     onDestroy : function(){
47630        
47631         if(this.store){
47632             this.store.un('beforeload', this.onBeforeLoad, this);
47633             this.store.un('load', this.onLoad, this);
47634             this.store.un('loadexception', this.onLoadException, this);
47635         }
47636         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47637     },
47638
47639     // private
47640     fireKey : function(e){
47641         if(e.isNavKeyPress() && !this.list.isVisible()){
47642             this.fireEvent("specialkey", this, e);
47643         }
47644     },
47645
47646     // private
47647     onResize: function(w, h){
47648         
47649         return; 
47650     
47651         
47652     },
47653
47654     /**
47655      * Allow or prevent the user from directly editing the field text.  If false is passed,
47656      * the user will only be able to select from the items defined in the dropdown list.  This method
47657      * is the runtime equivalent of setting the 'editable' config option at config time.
47658      * @param {Boolean} value True to allow the user to directly edit the field text
47659      */
47660     setEditable : function(value){
47661          
47662     },
47663
47664     // private
47665     onBeforeLoad : function(){
47666         
47667         Roo.log("Select before load");
47668         return;
47669     
47670         this.innerList.update(this.loadingText ?
47671                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47672         //this.restrictHeight();
47673         this.selectedIndex = -1;
47674     },
47675
47676     // private
47677     onLoad : function(){
47678
47679     
47680         var dom = this.el.dom;
47681         dom.innerHTML = '';
47682          var od = dom.ownerDocument;
47683          
47684         if (this.emptyText) {
47685             var op = od.createElement('option');
47686             op.setAttribute('value', '');
47687             op.innerHTML = String.format('{0}', this.emptyText);
47688             dom.appendChild(op);
47689         }
47690         if(this.store.getCount() > 0){
47691            
47692             var vf = this.valueField;
47693             var df = this.displayField;
47694             this.store.data.each(function(r) {
47695                 // which colmsn to use... testing - cdoe / title..
47696                 var op = od.createElement('option');
47697                 op.setAttribute('value', r.data[vf]);
47698                 op.innerHTML = String.format('{0}', r.data[df]);
47699                 dom.appendChild(op);
47700             });
47701             if (typeof(this.defaultValue != 'undefined')) {
47702                 this.setValue(this.defaultValue);
47703             }
47704             
47705              
47706         }else{
47707             //this.onEmptyResults();
47708         }
47709         //this.el.focus();
47710     },
47711     // private
47712     onLoadException : function()
47713     {
47714         dom.innerHTML = '';
47715             
47716         Roo.log("Select on load exception");
47717         return;
47718     
47719         this.collapse();
47720         Roo.log(this.store.reader.jsonData);
47721         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47722             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47723         }
47724         
47725         
47726     },
47727     // private
47728     onTypeAhead : function(){
47729          
47730     },
47731
47732     // private
47733     onSelect : function(record, index){
47734         Roo.log('on select?');
47735         return;
47736         if(this.fireEvent('beforeselect', this, record, index) !== false){
47737             this.setFromData(index > -1 ? record.data : false);
47738             this.collapse();
47739             this.fireEvent('select', this, record, index);
47740         }
47741     },
47742
47743     /**
47744      * Returns the currently selected field value or empty string if no value is set.
47745      * @return {String} value The selected value
47746      */
47747     getValue : function(){
47748         var dom = this.el.dom;
47749         this.value = dom.options[dom.selectedIndex].value;
47750         return this.value;
47751         
47752     },
47753
47754     /**
47755      * Clears any text/value currently set in the field
47756      */
47757     clearValue : function(){
47758         this.value = '';
47759         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47760         
47761     },
47762
47763     /**
47764      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47765      * will be displayed in the field.  If the value does not match the data value of an existing item,
47766      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47767      * Otherwise the field will be blank (although the value will still be set).
47768      * @param {String} value The value to match
47769      */
47770     setValue : function(v){
47771         var d = this.el.dom;
47772         for (var i =0; i < d.options.length;i++) {
47773             if (v == d.options[i].value) {
47774                 d.selectedIndex = i;
47775                 this.value = v;
47776                 return;
47777             }
47778         }
47779         this.clearValue();
47780     },
47781     /**
47782      * @property {Object} the last set data for the element
47783      */
47784     
47785     lastData : false,
47786     /**
47787      * Sets the value of the field based on a object which is related to the record format for the store.
47788      * @param {Object} value the value to set as. or false on reset?
47789      */
47790     setFromData : function(o){
47791         Roo.log('setfrom data?');
47792          
47793         
47794         
47795     },
47796     // private
47797     reset : function(){
47798         this.clearValue();
47799     },
47800     // private
47801     findRecord : function(prop, value){
47802         
47803         return false;
47804     
47805         var record;
47806         if(this.store.getCount() > 0){
47807             this.store.each(function(r){
47808                 if(r.data[prop] == value){
47809                     record = r;
47810                     return false;
47811                 }
47812                 return true;
47813             });
47814         }
47815         return record;
47816     },
47817     
47818     getName: function()
47819     {
47820         // returns hidden if it's set..
47821         if (!this.rendered) {return ''};
47822         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47823         
47824     },
47825      
47826
47827     
47828
47829     // private
47830     onEmptyResults : function(){
47831         Roo.log('empty results');
47832         //this.collapse();
47833     },
47834
47835     /**
47836      * Returns true if the dropdown list is expanded, else false.
47837      */
47838     isExpanded : function(){
47839         return false;
47840     },
47841
47842     /**
47843      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47844      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47845      * @param {String} value The data value of the item to select
47846      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47847      * selected item if it is not currently in view (defaults to true)
47848      * @return {Boolean} True if the value matched an item in the list, else false
47849      */
47850     selectByValue : function(v, scrollIntoView){
47851         Roo.log('select By Value');
47852         return false;
47853     
47854         if(v !== undefined && v !== null){
47855             var r = this.findRecord(this.valueField || this.displayField, v);
47856             if(r){
47857                 this.select(this.store.indexOf(r), scrollIntoView);
47858                 return true;
47859             }
47860         }
47861         return false;
47862     },
47863
47864     /**
47865      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47866      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47867      * @param {Number} index The zero-based index of the list item to select
47868      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47869      * selected item if it is not currently in view (defaults to true)
47870      */
47871     select : function(index, scrollIntoView){
47872         Roo.log('select ');
47873         return  ;
47874         
47875         this.selectedIndex = index;
47876         this.view.select(index);
47877         if(scrollIntoView !== false){
47878             var el = this.view.getNode(index);
47879             if(el){
47880                 this.innerList.scrollChildIntoView(el, false);
47881             }
47882         }
47883     },
47884
47885       
47886
47887     // private
47888     validateBlur : function(){
47889         
47890         return;
47891         
47892     },
47893
47894     // private
47895     initQuery : function(){
47896         this.doQuery(this.getRawValue());
47897     },
47898
47899     // private
47900     doForce : function(){
47901         if(this.el.dom.value.length > 0){
47902             this.el.dom.value =
47903                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47904              
47905         }
47906     },
47907
47908     /**
47909      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47910      * query allowing the query action to be canceled if needed.
47911      * @param {String} query The SQL query to execute
47912      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47913      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47914      * saved in the current store (defaults to false)
47915      */
47916     doQuery : function(q, forceAll){
47917         
47918         Roo.log('doQuery?');
47919         if(q === undefined || q === null){
47920             q = '';
47921         }
47922         var qe = {
47923             query: q,
47924             forceAll: forceAll,
47925             combo: this,
47926             cancel:false
47927         };
47928         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47929             return false;
47930         }
47931         q = qe.query;
47932         forceAll = qe.forceAll;
47933         if(forceAll === true || (q.length >= this.minChars)){
47934             if(this.lastQuery != q || this.alwaysQuery){
47935                 this.lastQuery = q;
47936                 if(this.mode == 'local'){
47937                     this.selectedIndex = -1;
47938                     if(forceAll){
47939                         this.store.clearFilter();
47940                     }else{
47941                         this.store.filter(this.displayField, q);
47942                     }
47943                     this.onLoad();
47944                 }else{
47945                     this.store.baseParams[this.queryParam] = q;
47946                     this.store.load({
47947                         params: this.getParams(q)
47948                     });
47949                     this.expand();
47950                 }
47951             }else{
47952                 this.selectedIndex = -1;
47953                 this.onLoad();   
47954             }
47955         }
47956     },
47957
47958     // private
47959     getParams : function(q){
47960         var p = {};
47961         //p[this.queryParam] = q;
47962         if(this.pageSize){
47963             p.start = 0;
47964             p.limit = this.pageSize;
47965         }
47966         return p;
47967     },
47968
47969     /**
47970      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47971      */
47972     collapse : function(){
47973         
47974     },
47975
47976     // private
47977     collapseIf : function(e){
47978         
47979     },
47980
47981     /**
47982      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47983      */
47984     expand : function(){
47985         
47986     } ,
47987
47988     // private
47989      
47990
47991     /** 
47992     * @cfg {Boolean} grow 
47993     * @hide 
47994     */
47995     /** 
47996     * @cfg {Number} growMin 
47997     * @hide 
47998     */
47999     /** 
48000     * @cfg {Number} growMax 
48001     * @hide 
48002     */
48003     /**
48004      * @hide
48005      * @method autoSize
48006      */
48007     
48008     setWidth : function()
48009     {
48010         
48011     },
48012     getResizeEl : function(){
48013         return this.el;
48014     }
48015 });//<script type="text/javasscript">
48016  
48017
48018 /**
48019  * @class Roo.DDView
48020  * A DnD enabled version of Roo.View.
48021  * @param {Element/String} container The Element in which to create the View.
48022  * @param {String} tpl The template string used to create the markup for each element of the View
48023  * @param {Object} config The configuration properties. These include all the config options of
48024  * {@link Roo.View} plus some specific to this class.<br>
48025  * <p>
48026  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48027  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48028  * <p>
48029  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48030 .x-view-drag-insert-above {
48031         border-top:1px dotted #3366cc;
48032 }
48033 .x-view-drag-insert-below {
48034         border-bottom:1px dotted #3366cc;
48035 }
48036 </code></pre>
48037  * 
48038  */
48039  
48040 Roo.DDView = function(container, tpl, config) {
48041     Roo.DDView.superclass.constructor.apply(this, arguments);
48042     this.getEl().setStyle("outline", "0px none");
48043     this.getEl().unselectable();
48044     if (this.dragGroup) {
48045                 this.setDraggable(this.dragGroup.split(","));
48046     }
48047     if (this.dropGroup) {
48048                 this.setDroppable(this.dropGroup.split(","));
48049     }
48050     if (this.deletable) {
48051         this.setDeletable();
48052     }
48053     this.isDirtyFlag = false;
48054         this.addEvents({
48055                 "drop" : true
48056         });
48057 };
48058
48059 Roo.extend(Roo.DDView, Roo.View, {
48060 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48061 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48062 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48063 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48064
48065         isFormField: true,
48066
48067         reset: Roo.emptyFn,
48068         
48069         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48070
48071         validate: function() {
48072                 return true;
48073         },
48074         
48075         destroy: function() {
48076                 this.purgeListeners();
48077                 this.getEl.removeAllListeners();
48078                 this.getEl().remove();
48079                 if (this.dragZone) {
48080                         if (this.dragZone.destroy) {
48081                                 this.dragZone.destroy();
48082                         }
48083                 }
48084                 if (this.dropZone) {
48085                         if (this.dropZone.destroy) {
48086                                 this.dropZone.destroy();
48087                         }
48088                 }
48089         },
48090
48091 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48092         getName: function() {
48093                 return this.name;
48094         },
48095
48096 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48097         setValue: function(v) {
48098                 if (!this.store) {
48099                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48100                 }
48101                 var data = {};
48102                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48103                 this.store.proxy = new Roo.data.MemoryProxy(data);
48104                 this.store.load();
48105         },
48106
48107 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48108         getValue: function() {
48109                 var result = '(';
48110                 this.store.each(function(rec) {
48111                         result += rec.id + ',';
48112                 });
48113                 return result.substr(0, result.length - 1) + ')';
48114         },
48115         
48116         getIds: function() {
48117                 var i = 0, result = new Array(this.store.getCount());
48118                 this.store.each(function(rec) {
48119                         result[i++] = rec.id;
48120                 });
48121                 return result;
48122         },
48123         
48124         isDirty: function() {
48125                 return this.isDirtyFlag;
48126         },
48127
48128 /**
48129  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48130  *      whole Element becomes the target, and this causes the drop gesture to append.
48131  */
48132     getTargetFromEvent : function(e) {
48133                 var target = e.getTarget();
48134                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48135                 target = target.parentNode;
48136                 }
48137                 if (!target) {
48138                         target = this.el.dom.lastChild || this.el.dom;
48139                 }
48140                 return target;
48141     },
48142
48143 /**
48144  *      Create the drag data which consists of an object which has the property "ddel" as
48145  *      the drag proxy element. 
48146  */
48147     getDragData : function(e) {
48148         var target = this.findItemFromChild(e.getTarget());
48149                 if(target) {
48150                         this.handleSelection(e);
48151                         var selNodes = this.getSelectedNodes();
48152             var dragData = {
48153                 source: this,
48154                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48155                 nodes: selNodes,
48156                 records: []
48157                         };
48158                         var selectedIndices = this.getSelectedIndexes();
48159                         for (var i = 0; i < selectedIndices.length; i++) {
48160                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48161                         }
48162                         if (selNodes.length == 1) {
48163                                 dragData.ddel = target.cloneNode(true); // the div element
48164                         } else {
48165                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48166                                 div.className = 'multi-proxy';
48167                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48168                                         div.appendChild(selNodes[i].cloneNode(true));
48169                                 }
48170                                 dragData.ddel = div;
48171                         }
48172             //console.log(dragData)
48173             //console.log(dragData.ddel.innerHTML)
48174                         return dragData;
48175                 }
48176         //console.log('nodragData')
48177                 return false;
48178     },
48179     
48180 /**     Specify to which ddGroup items in this DDView may be dragged. */
48181     setDraggable: function(ddGroup) {
48182         if (ddGroup instanceof Array) {
48183                 Roo.each(ddGroup, this.setDraggable, this);
48184                 return;
48185         }
48186         if (this.dragZone) {
48187                 this.dragZone.addToGroup(ddGroup);
48188         } else {
48189                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48190                                 containerScroll: true,
48191                                 ddGroup: ddGroup 
48192
48193                         });
48194 //                      Draggability implies selection. DragZone's mousedown selects the element.
48195                         if (!this.multiSelect) { this.singleSelect = true; }
48196
48197 //                      Wire the DragZone's handlers up to methods in *this*
48198                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48199                 }
48200     },
48201
48202 /**     Specify from which ddGroup this DDView accepts drops. */
48203     setDroppable: function(ddGroup) {
48204         if (ddGroup instanceof Array) {
48205                 Roo.each(ddGroup, this.setDroppable, this);
48206                 return;
48207         }
48208         if (this.dropZone) {
48209                 this.dropZone.addToGroup(ddGroup);
48210         } else {
48211                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48212                                 containerScroll: true,
48213                                 ddGroup: ddGroup
48214                         });
48215
48216 //                      Wire the DropZone's handlers up to methods in *this*
48217                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48218                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48219                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48220                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48221                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48222                 }
48223     },
48224
48225 /**     Decide whether to drop above or below a View node. */
48226     getDropPoint : function(e, n, dd){
48227         if (n == this.el.dom) { return "above"; }
48228                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48229                 var c = t + (b - t) / 2;
48230                 var y = Roo.lib.Event.getPageY(e);
48231                 if(y <= c) {
48232                         return "above";
48233                 }else{
48234                         return "below";
48235                 }
48236     },
48237
48238     onNodeEnter : function(n, dd, e, data){
48239                 return false;
48240     },
48241     
48242     onNodeOver : function(n, dd, e, data){
48243                 var pt = this.getDropPoint(e, n, dd);
48244                 // set the insert point style on the target node
48245                 var dragElClass = this.dropNotAllowed;
48246                 if (pt) {
48247                         var targetElClass;
48248                         if (pt == "above"){
48249                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48250                                 targetElClass = "x-view-drag-insert-above";
48251                         } else {
48252                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48253                                 targetElClass = "x-view-drag-insert-below";
48254                         }
48255                         if (this.lastInsertClass != targetElClass){
48256                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48257                                 this.lastInsertClass = targetElClass;
48258                         }
48259                 }
48260                 return dragElClass;
48261         },
48262
48263     onNodeOut : function(n, dd, e, data){
48264                 this.removeDropIndicators(n);
48265     },
48266
48267     onNodeDrop : function(n, dd, e, data){
48268         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48269                 return false;
48270         }
48271         var pt = this.getDropPoint(e, n, dd);
48272                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48273                 if (pt == "below") { insertAt++; }
48274                 for (var i = 0; i < data.records.length; i++) {
48275                         var r = data.records[i];
48276                         var dup = this.store.getById(r.id);
48277                         if (dup && (dd != this.dragZone)) {
48278                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48279                         } else {
48280                                 if (data.copy) {
48281                                         this.store.insert(insertAt++, r.copy());
48282                                 } else {
48283                                         data.source.isDirtyFlag = true;
48284                                         r.store.remove(r);
48285                                         this.store.insert(insertAt++, r);
48286                                 }
48287                                 this.isDirtyFlag = true;
48288                         }
48289                 }
48290                 this.dragZone.cachedTarget = null;
48291                 return true;
48292     },
48293
48294     removeDropIndicators : function(n){
48295                 if(n){
48296                         Roo.fly(n).removeClass([
48297                                 "x-view-drag-insert-above",
48298                                 "x-view-drag-insert-below"]);
48299                         this.lastInsertClass = "_noclass";
48300                 }
48301     },
48302
48303 /**
48304  *      Utility method. Add a delete option to the DDView's context menu.
48305  *      @param {String} imageUrl The URL of the "delete" icon image.
48306  */
48307         setDeletable: function(imageUrl) {
48308                 if (!this.singleSelect && !this.multiSelect) {
48309                         this.singleSelect = true;
48310                 }
48311                 var c = this.getContextMenu();
48312                 this.contextMenu.on("itemclick", function(item) {
48313                         switch (item.id) {
48314                                 case "delete":
48315                                         this.remove(this.getSelectedIndexes());
48316                                         break;
48317                         }
48318                 }, this);
48319                 this.contextMenu.add({
48320                         icon: imageUrl,
48321                         id: "delete",
48322                         text: 'Delete'
48323                 });
48324         },
48325         
48326 /**     Return the context menu for this DDView. */
48327         getContextMenu: function() {
48328                 if (!this.contextMenu) {
48329 //                      Create the View's context menu
48330                         this.contextMenu = new Roo.menu.Menu({
48331                                 id: this.id + "-contextmenu"
48332                         });
48333                         this.el.on("contextmenu", this.showContextMenu, this);
48334                 }
48335                 return this.contextMenu;
48336         },
48337         
48338         disableContextMenu: function() {
48339                 if (this.contextMenu) {
48340                         this.el.un("contextmenu", this.showContextMenu, this);
48341                 }
48342         },
48343
48344         showContextMenu: function(e, item) {
48345         item = this.findItemFromChild(e.getTarget());
48346                 if (item) {
48347                         e.stopEvent();
48348                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48349                         this.contextMenu.showAt(e.getXY());
48350             }
48351     },
48352
48353 /**
48354  *      Remove {@link Roo.data.Record}s at the specified indices.
48355  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48356  */
48357     remove: function(selectedIndices) {
48358                 selectedIndices = [].concat(selectedIndices);
48359                 for (var i = 0; i < selectedIndices.length; i++) {
48360                         var rec = this.store.getAt(selectedIndices[i]);
48361                         this.store.remove(rec);
48362                 }
48363     },
48364
48365 /**
48366  *      Double click fires the event, but also, if this is draggable, and there is only one other
48367  *      related DropZone, it transfers the selected node.
48368  */
48369     onDblClick : function(e){
48370         var item = this.findItemFromChild(e.getTarget());
48371         if(item){
48372             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48373                 return false;
48374             }
48375             if (this.dragGroup) {
48376                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48377                     while (targets.indexOf(this.dropZone) > -1) {
48378                             targets.remove(this.dropZone);
48379                                 }
48380                     if (targets.length == 1) {
48381                                         this.dragZone.cachedTarget = null;
48382                         var el = Roo.get(targets[0].getEl());
48383                         var box = el.getBox(true);
48384                         targets[0].onNodeDrop(el.dom, {
48385                                 target: el.dom,
48386                                 xy: [box.x, box.y + box.height - 1]
48387                         }, null, this.getDragData(e));
48388                     }
48389                 }
48390         }
48391     },
48392     
48393     handleSelection: function(e) {
48394                 this.dragZone.cachedTarget = null;
48395         var item = this.findItemFromChild(e.getTarget());
48396         if (!item) {
48397                 this.clearSelections(true);
48398                 return;
48399         }
48400                 if (item && (this.multiSelect || this.singleSelect)){
48401                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48402                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48403                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48404                                 this.unselect(item);
48405                         } else {
48406                                 this.select(item, this.multiSelect && e.ctrlKey);
48407                                 this.lastSelection = item;
48408                         }
48409                 }
48410     },
48411
48412     onItemClick : function(item, index, e){
48413                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48414                         return false;
48415                 }
48416                 return true;
48417     },
48418
48419     unselect : function(nodeInfo, suppressEvent){
48420                 var node = this.getNode(nodeInfo);
48421                 if(node && this.isSelected(node)){
48422                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48423                                 Roo.fly(node).removeClass(this.selectedClass);
48424                                 this.selections.remove(node);
48425                                 if(!suppressEvent){
48426                                         this.fireEvent("selectionchange", this, this.selections);
48427                                 }
48428                         }
48429                 }
48430     }
48431 });
48432 /*
48433  * Based on:
48434  * Ext JS Library 1.1.1
48435  * Copyright(c) 2006-2007, Ext JS, LLC.
48436  *
48437  * Originally Released Under LGPL - original licence link has changed is not relivant.
48438  *
48439  * Fork - LGPL
48440  * <script type="text/javascript">
48441  */
48442  
48443 /**
48444  * @class Roo.LayoutManager
48445  * @extends Roo.util.Observable
48446  * Base class for layout managers.
48447  */
48448 Roo.LayoutManager = function(container, config){
48449     Roo.LayoutManager.superclass.constructor.call(this);
48450     this.el = Roo.get(container);
48451     // ie scrollbar fix
48452     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48453         document.body.scroll = "no";
48454     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48455         this.el.position('relative');
48456     }
48457     this.id = this.el.id;
48458     this.el.addClass("x-layout-container");
48459     /** false to disable window resize monitoring @type Boolean */
48460     this.monitorWindowResize = true;
48461     this.regions = {};
48462     this.addEvents({
48463         /**
48464          * @event layout
48465          * Fires when a layout is performed. 
48466          * @param {Roo.LayoutManager} this
48467          */
48468         "layout" : true,
48469         /**
48470          * @event regionresized
48471          * Fires when the user resizes a region. 
48472          * @param {Roo.LayoutRegion} region The resized region
48473          * @param {Number} newSize The new size (width for east/west, height for north/south)
48474          */
48475         "regionresized" : true,
48476         /**
48477          * @event regioncollapsed
48478          * Fires when a region is collapsed. 
48479          * @param {Roo.LayoutRegion} region The collapsed region
48480          */
48481         "regioncollapsed" : true,
48482         /**
48483          * @event regionexpanded
48484          * Fires when a region is expanded.  
48485          * @param {Roo.LayoutRegion} region The expanded region
48486          */
48487         "regionexpanded" : true
48488     });
48489     this.updating = false;
48490     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48491 };
48492
48493 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48494     /**
48495      * Returns true if this layout is currently being updated
48496      * @return {Boolean}
48497      */
48498     isUpdating : function(){
48499         return this.updating; 
48500     },
48501     
48502     /**
48503      * Suspend the LayoutManager from doing auto-layouts while
48504      * making multiple add or remove calls
48505      */
48506     beginUpdate : function(){
48507         this.updating = true;    
48508     },
48509     
48510     /**
48511      * Restore auto-layouts and optionally disable the manager from performing a layout
48512      * @param {Boolean} noLayout true to disable a layout update 
48513      */
48514     endUpdate : function(noLayout){
48515         this.updating = false;
48516         if(!noLayout){
48517             this.layout();
48518         }    
48519     },
48520     
48521     layout: function(){
48522         
48523     },
48524     
48525     onRegionResized : function(region, newSize){
48526         this.fireEvent("regionresized", region, newSize);
48527         this.layout();
48528     },
48529     
48530     onRegionCollapsed : function(region){
48531         this.fireEvent("regioncollapsed", region);
48532     },
48533     
48534     onRegionExpanded : function(region){
48535         this.fireEvent("regionexpanded", region);
48536     },
48537         
48538     /**
48539      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48540      * performs box-model adjustments.
48541      * @return {Object} The size as an object {width: (the width), height: (the height)}
48542      */
48543     getViewSize : function(){
48544         var size;
48545         if(this.el.dom != document.body){
48546             size = this.el.getSize();
48547         }else{
48548             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48549         }
48550         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48551         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48552         return size;
48553     },
48554     
48555     /**
48556      * Returns the Element this layout is bound to.
48557      * @return {Roo.Element}
48558      */
48559     getEl : function(){
48560         return this.el;
48561     },
48562     
48563     /**
48564      * Returns the specified region.
48565      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48566      * @return {Roo.LayoutRegion}
48567      */
48568     getRegion : function(target){
48569         return this.regions[target.toLowerCase()];
48570     },
48571     
48572     onWindowResize : function(){
48573         if(this.monitorWindowResize){
48574             this.layout();
48575         }
48576     }
48577 });/*
48578  * Based on:
48579  * Ext JS Library 1.1.1
48580  * Copyright(c) 2006-2007, Ext JS, LLC.
48581  *
48582  * Originally Released Under LGPL - original licence link has changed is not relivant.
48583  *
48584  * Fork - LGPL
48585  * <script type="text/javascript">
48586  */
48587 /**
48588  * @class Roo.BorderLayout
48589  * @extends Roo.LayoutManager
48590  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48591  * please see: <br><br>
48592  * <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>
48593  * <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>
48594  * Example:
48595  <pre><code>
48596  var layout = new Roo.BorderLayout(document.body, {
48597     north: {
48598         initialSize: 25,
48599         titlebar: false
48600     },
48601     west: {
48602         split:true,
48603         initialSize: 200,
48604         minSize: 175,
48605         maxSize: 400,
48606         titlebar: true,
48607         collapsible: true
48608     },
48609     east: {
48610         split:true,
48611         initialSize: 202,
48612         minSize: 175,
48613         maxSize: 400,
48614         titlebar: true,
48615         collapsible: true
48616     },
48617     south: {
48618         split:true,
48619         initialSize: 100,
48620         minSize: 100,
48621         maxSize: 200,
48622         titlebar: true,
48623         collapsible: true
48624     },
48625     center: {
48626         titlebar: true,
48627         autoScroll:true,
48628         resizeTabs: true,
48629         minTabWidth: 50,
48630         preferredTabWidth: 150
48631     }
48632 });
48633
48634 // shorthand
48635 var CP = Roo.ContentPanel;
48636
48637 layout.beginUpdate();
48638 layout.add("north", new CP("north", "North"));
48639 layout.add("south", new CP("south", {title: "South", closable: true}));
48640 layout.add("west", new CP("west", {title: "West"}));
48641 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48642 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48643 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48644 layout.getRegion("center").showPanel("center1");
48645 layout.endUpdate();
48646 </code></pre>
48647
48648 <b>The container the layout is rendered into can be either the body element or any other element.
48649 If it is not the body element, the container needs to either be an absolute positioned element,
48650 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48651 the container size if it is not the body element.</b>
48652
48653 * @constructor
48654 * Create a new BorderLayout
48655 * @param {String/HTMLElement/Element} container The container this layout is bound to
48656 * @param {Object} config Configuration options
48657  */
48658 Roo.BorderLayout = function(container, config){
48659     config = config || {};
48660     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48661     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48662     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48663         var target = this.factory.validRegions[i];
48664         if(config[target]){
48665             this.addRegion(target, config[target]);
48666         }
48667     }
48668 };
48669
48670 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48671     /**
48672      * Creates and adds a new region if it doesn't already exist.
48673      * @param {String} target The target region key (north, south, east, west or center).
48674      * @param {Object} config The regions config object
48675      * @return {BorderLayoutRegion} The new region
48676      */
48677     addRegion : function(target, config){
48678         if(!this.regions[target]){
48679             var r = this.factory.create(target, this, config);
48680             this.bindRegion(target, r);
48681         }
48682         return this.regions[target];
48683     },
48684
48685     // private (kinda)
48686     bindRegion : function(name, r){
48687         this.regions[name] = r;
48688         r.on("visibilitychange", this.layout, this);
48689         r.on("paneladded", this.layout, this);
48690         r.on("panelremoved", this.layout, this);
48691         r.on("invalidated", this.layout, this);
48692         r.on("resized", this.onRegionResized, this);
48693         r.on("collapsed", this.onRegionCollapsed, this);
48694         r.on("expanded", this.onRegionExpanded, this);
48695     },
48696
48697     /**
48698      * Performs a layout update.
48699      */
48700     layout : function(){
48701         if(this.updating) return;
48702         var size = this.getViewSize();
48703         var w = size.width;
48704         var h = size.height;
48705         var centerW = w;
48706         var centerH = h;
48707         var centerY = 0;
48708         var centerX = 0;
48709         //var x = 0, y = 0;
48710
48711         var rs = this.regions;
48712         var north = rs["north"];
48713         var south = rs["south"]; 
48714         var west = rs["west"];
48715         var east = rs["east"];
48716         var center = rs["center"];
48717         //if(this.hideOnLayout){ // not supported anymore
48718             //c.el.setStyle("display", "none");
48719         //}
48720         if(north && north.isVisible()){
48721             var b = north.getBox();
48722             var m = north.getMargins();
48723             b.width = w - (m.left+m.right);
48724             b.x = m.left;
48725             b.y = m.top;
48726             centerY = b.height + b.y + m.bottom;
48727             centerH -= centerY;
48728             north.updateBox(this.safeBox(b));
48729         }
48730         if(south && south.isVisible()){
48731             var b = south.getBox();
48732             var m = south.getMargins();
48733             b.width = w - (m.left+m.right);
48734             b.x = m.left;
48735             var totalHeight = (b.height + m.top + m.bottom);
48736             b.y = h - totalHeight + m.top;
48737             centerH -= totalHeight;
48738             south.updateBox(this.safeBox(b));
48739         }
48740         if(west && west.isVisible()){
48741             var b = west.getBox();
48742             var m = west.getMargins();
48743             b.height = centerH - (m.top+m.bottom);
48744             b.x = m.left;
48745             b.y = centerY + m.top;
48746             var totalWidth = (b.width + m.left + m.right);
48747             centerX += totalWidth;
48748             centerW -= totalWidth;
48749             west.updateBox(this.safeBox(b));
48750         }
48751         if(east && east.isVisible()){
48752             var b = east.getBox();
48753             var m = east.getMargins();
48754             b.height = centerH - (m.top+m.bottom);
48755             var totalWidth = (b.width + m.left + m.right);
48756             b.x = w - totalWidth + m.left;
48757             b.y = centerY + m.top;
48758             centerW -= totalWidth;
48759             east.updateBox(this.safeBox(b));
48760         }
48761         if(center){
48762             var m = center.getMargins();
48763             var centerBox = {
48764                 x: centerX + m.left,
48765                 y: centerY + m.top,
48766                 width: centerW - (m.left+m.right),
48767                 height: centerH - (m.top+m.bottom)
48768             };
48769             //if(this.hideOnLayout){
48770                 //center.el.setStyle("display", "block");
48771             //}
48772             center.updateBox(this.safeBox(centerBox));
48773         }
48774         this.el.repaint();
48775         this.fireEvent("layout", this);
48776     },
48777
48778     // private
48779     safeBox : function(box){
48780         box.width = Math.max(0, box.width);
48781         box.height = Math.max(0, box.height);
48782         return box;
48783     },
48784
48785     /**
48786      * Adds a ContentPanel (or subclass) to this layout.
48787      * @param {String} target The target region key (north, south, east, west or center).
48788      * @param {Roo.ContentPanel} panel The panel to add
48789      * @return {Roo.ContentPanel} The added panel
48790      */
48791     add : function(target, panel){
48792          
48793         target = target.toLowerCase();
48794         return this.regions[target].add(panel);
48795     },
48796
48797     /**
48798      * Remove a ContentPanel (or subclass) to this layout.
48799      * @param {String} target The target region key (north, south, east, west or center).
48800      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48801      * @return {Roo.ContentPanel} The removed panel
48802      */
48803     remove : function(target, panel){
48804         target = target.toLowerCase();
48805         return this.regions[target].remove(panel);
48806     },
48807
48808     /**
48809      * Searches all regions for a panel with the specified id
48810      * @param {String} panelId
48811      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48812      */
48813     findPanel : function(panelId){
48814         var rs = this.regions;
48815         for(var target in rs){
48816             if(typeof rs[target] != "function"){
48817                 var p = rs[target].getPanel(panelId);
48818                 if(p){
48819                     return p;
48820                 }
48821             }
48822         }
48823         return null;
48824     },
48825
48826     /**
48827      * Searches all regions for a panel with the specified id and activates (shows) it.
48828      * @param {String/ContentPanel} panelId The panels id or the panel itself
48829      * @return {Roo.ContentPanel} The shown panel or null
48830      */
48831     showPanel : function(panelId) {
48832       var rs = this.regions;
48833       for(var target in rs){
48834          var r = rs[target];
48835          if(typeof r != "function"){
48836             if(r.hasPanel(panelId)){
48837                return r.showPanel(panelId);
48838             }
48839          }
48840       }
48841       return null;
48842    },
48843
48844    /**
48845      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48846      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48847      */
48848     restoreState : function(provider){
48849         if(!provider){
48850             provider = Roo.state.Manager;
48851         }
48852         var sm = new Roo.LayoutStateManager();
48853         sm.init(this, provider);
48854     },
48855
48856     /**
48857      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48858      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48859      * a valid ContentPanel config object.  Example:
48860      * <pre><code>
48861 // Create the main layout
48862 var layout = new Roo.BorderLayout('main-ct', {
48863     west: {
48864         split:true,
48865         minSize: 175,
48866         titlebar: true
48867     },
48868     center: {
48869         title:'Components'
48870     }
48871 }, 'main-ct');
48872
48873 // Create and add multiple ContentPanels at once via configs
48874 layout.batchAdd({
48875    west: {
48876        id: 'source-files',
48877        autoCreate:true,
48878        title:'Ext Source Files',
48879        autoScroll:true,
48880        fitToFrame:true
48881    },
48882    center : {
48883        el: cview,
48884        autoScroll:true,
48885        fitToFrame:true,
48886        toolbar: tb,
48887        resizeEl:'cbody'
48888    }
48889 });
48890 </code></pre>
48891      * @param {Object} regions An object containing ContentPanel configs by region name
48892      */
48893     batchAdd : function(regions){
48894         this.beginUpdate();
48895         for(var rname in regions){
48896             var lr = this.regions[rname];
48897             if(lr){
48898                 this.addTypedPanels(lr, regions[rname]);
48899             }
48900         }
48901         this.endUpdate();
48902     },
48903
48904     // private
48905     addTypedPanels : function(lr, ps){
48906         if(typeof ps == 'string'){
48907             lr.add(new Roo.ContentPanel(ps));
48908         }
48909         else if(ps instanceof Array){
48910             for(var i =0, len = ps.length; i < len; i++){
48911                 this.addTypedPanels(lr, ps[i]);
48912             }
48913         }
48914         else if(!ps.events){ // raw config?
48915             var el = ps.el;
48916             delete ps.el; // prevent conflict
48917             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48918         }
48919         else {  // panel object assumed!
48920             lr.add(ps);
48921         }
48922     },
48923     /**
48924      * Adds a xtype elements to the layout.
48925      * <pre><code>
48926
48927 layout.addxtype({
48928        xtype : 'ContentPanel',
48929        region: 'west',
48930        items: [ .... ]
48931    }
48932 );
48933
48934 layout.addxtype({
48935         xtype : 'NestedLayoutPanel',
48936         region: 'west',
48937         layout: {
48938            center: { },
48939            west: { }   
48940         },
48941         items : [ ... list of content panels or nested layout panels.. ]
48942    }
48943 );
48944 </code></pre>
48945      * @param {Object} cfg Xtype definition of item to add.
48946      */
48947     addxtype : function(cfg)
48948     {
48949         // basically accepts a pannel...
48950         // can accept a layout region..!?!?
48951         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48952         
48953         if (!cfg.xtype.match(/Panel$/)) {
48954             return false;
48955         }
48956         var ret = false;
48957         
48958         if (typeof(cfg.region) == 'undefined') {
48959             Roo.log("Failed to add Panel, region was not set");
48960             Roo.log(cfg);
48961             return false;
48962         }
48963         var region = cfg.region;
48964         delete cfg.region;
48965         
48966           
48967         var xitems = [];
48968         if (cfg.items) {
48969             xitems = cfg.items;
48970             delete cfg.items;
48971         }
48972         var nb = false;
48973         
48974         switch(cfg.xtype) 
48975         {
48976             case 'ContentPanel':  // ContentPanel (el, cfg)
48977             case 'ScrollPanel':  // ContentPanel (el, cfg)
48978             case 'ViewPanel': 
48979                 if(cfg.autoCreate) {
48980                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48981                 } else {
48982                     var el = this.el.createChild();
48983                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48984                 }
48985                 
48986                 this.add(region, ret);
48987                 break;
48988             
48989             
48990             case 'TreePanel': // our new panel!
48991                 cfg.el = this.el.createChild();
48992                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48993                 this.add(region, ret);
48994                 break;
48995             
48996             case 'NestedLayoutPanel': 
48997                 // create a new Layout (which is  a Border Layout...
48998                 var el = this.el.createChild();
48999                 var clayout = cfg.layout;
49000                 delete cfg.layout;
49001                 clayout.items   = clayout.items  || [];
49002                 // replace this exitems with the clayout ones..
49003                 xitems = clayout.items;
49004                  
49005                 
49006                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49007                     cfg.background = false;
49008                 }
49009                 var layout = new Roo.BorderLayout(el, clayout);
49010                 
49011                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49012                 //console.log('adding nested layout panel '  + cfg.toSource());
49013                 this.add(region, ret);
49014                 nb = {}; /// find first...
49015                 break;
49016                 
49017             case 'GridPanel': 
49018             
49019                 // needs grid and region
49020                 
49021                 //var el = this.getRegion(region).el.createChild();
49022                 var el = this.el.createChild();
49023                 // create the grid first...
49024                 
49025                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49026                 delete cfg.grid;
49027                 if (region == 'center' && this.active ) {
49028                     cfg.background = false;
49029                 }
49030                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49031                 
49032                 this.add(region, ret);
49033                 if (cfg.background) {
49034                     ret.on('activate', function(gp) {
49035                         if (!gp.grid.rendered) {
49036                             gp.grid.render();
49037                         }
49038                     });
49039                 } else {
49040                     grid.render();
49041                 }
49042                 break;
49043            
49044            
49045            
49046                 
49047                 
49048                 
49049             default:
49050                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49051                     
49052                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49053                     this.add(region, ret);
49054                 } else {
49055                 
49056                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49057                     return null;
49058                 }
49059                 
49060              // GridPanel (grid, cfg)
49061             
49062         }
49063         this.beginUpdate();
49064         // add children..
49065         var region = '';
49066         var abn = {};
49067         Roo.each(xitems, function(i)  {
49068             region = nb && i.region ? i.region : false;
49069             
49070             var add = ret.addxtype(i);
49071            
49072             if (region) {
49073                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49074                 if (!i.background) {
49075                     abn[region] = nb[region] ;
49076                 }
49077             }
49078             
49079         });
49080         this.endUpdate();
49081
49082         // make the last non-background panel active..
49083         //if (nb) { Roo.log(abn); }
49084         if (nb) {
49085             
49086             for(var r in abn) {
49087                 region = this.getRegion(r);
49088                 if (region) {
49089                     // tried using nb[r], but it does not work..
49090                      
49091                     region.showPanel(abn[r]);
49092                    
49093                 }
49094             }
49095         }
49096         return ret;
49097         
49098     }
49099 });
49100
49101 /**
49102  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49103  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49104  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49105  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49106  * <pre><code>
49107 // shorthand
49108 var CP = Roo.ContentPanel;
49109
49110 var layout = Roo.BorderLayout.create({
49111     north: {
49112         initialSize: 25,
49113         titlebar: false,
49114         panels: [new CP("north", "North")]
49115     },
49116     west: {
49117         split:true,
49118         initialSize: 200,
49119         minSize: 175,
49120         maxSize: 400,
49121         titlebar: true,
49122         collapsible: true,
49123         panels: [new CP("west", {title: "West"})]
49124     },
49125     east: {
49126         split:true,
49127         initialSize: 202,
49128         minSize: 175,
49129         maxSize: 400,
49130         titlebar: true,
49131         collapsible: true,
49132         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49133     },
49134     south: {
49135         split:true,
49136         initialSize: 100,
49137         minSize: 100,
49138         maxSize: 200,
49139         titlebar: true,
49140         collapsible: true,
49141         panels: [new CP("south", {title: "South", closable: true})]
49142     },
49143     center: {
49144         titlebar: true,
49145         autoScroll:true,
49146         resizeTabs: true,
49147         minTabWidth: 50,
49148         preferredTabWidth: 150,
49149         panels: [
49150             new CP("center1", {title: "Close Me", closable: true}),
49151             new CP("center2", {title: "Center Panel", closable: false})
49152         ]
49153     }
49154 }, document.body);
49155
49156 layout.getRegion("center").showPanel("center1");
49157 </code></pre>
49158  * @param config
49159  * @param targetEl
49160  */
49161 Roo.BorderLayout.create = function(config, targetEl){
49162     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49163     layout.beginUpdate();
49164     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49165     for(var j = 0, jlen = regions.length; j < jlen; j++){
49166         var lr = regions[j];
49167         if(layout.regions[lr] && config[lr].panels){
49168             var r = layout.regions[lr];
49169             var ps = config[lr].panels;
49170             layout.addTypedPanels(r, ps);
49171         }
49172     }
49173     layout.endUpdate();
49174     return layout;
49175 };
49176
49177 // private
49178 Roo.BorderLayout.RegionFactory = {
49179     // private
49180     validRegions : ["north","south","east","west","center"],
49181
49182     // private
49183     create : function(target, mgr, config){
49184         target = target.toLowerCase();
49185         if(config.lightweight || config.basic){
49186             return new Roo.BasicLayoutRegion(mgr, config, target);
49187         }
49188         switch(target){
49189             case "north":
49190                 return new Roo.NorthLayoutRegion(mgr, config);
49191             case "south":
49192                 return new Roo.SouthLayoutRegion(mgr, config);
49193             case "east":
49194                 return new Roo.EastLayoutRegion(mgr, config);
49195             case "west":
49196                 return new Roo.WestLayoutRegion(mgr, config);
49197             case "center":
49198                 return new Roo.CenterLayoutRegion(mgr, config);
49199         }
49200         throw 'Layout region "'+target+'" not supported.';
49201     }
49202 };/*
49203  * Based on:
49204  * Ext JS Library 1.1.1
49205  * Copyright(c) 2006-2007, Ext JS, LLC.
49206  *
49207  * Originally Released Under LGPL - original licence link has changed is not relivant.
49208  *
49209  * Fork - LGPL
49210  * <script type="text/javascript">
49211  */
49212  
49213 /**
49214  * @class Roo.BasicLayoutRegion
49215  * @extends Roo.util.Observable
49216  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49217  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49218  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49219  */
49220 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49221     this.mgr = mgr;
49222     this.position  = pos;
49223     this.events = {
49224         /**
49225          * @scope Roo.BasicLayoutRegion
49226          */
49227         
49228         /**
49229          * @event beforeremove
49230          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49231          * @param {Roo.LayoutRegion} this
49232          * @param {Roo.ContentPanel} panel The panel
49233          * @param {Object} e The cancel event object
49234          */
49235         "beforeremove" : true,
49236         /**
49237          * @event invalidated
49238          * Fires when the layout for this region is changed.
49239          * @param {Roo.LayoutRegion} this
49240          */
49241         "invalidated" : true,
49242         /**
49243          * @event visibilitychange
49244          * Fires when this region is shown or hidden 
49245          * @param {Roo.LayoutRegion} this
49246          * @param {Boolean} visibility true or false
49247          */
49248         "visibilitychange" : true,
49249         /**
49250          * @event paneladded
49251          * Fires when a panel is added. 
49252          * @param {Roo.LayoutRegion} this
49253          * @param {Roo.ContentPanel} panel The panel
49254          */
49255         "paneladded" : true,
49256         /**
49257          * @event panelremoved
49258          * Fires when a panel is removed. 
49259          * @param {Roo.LayoutRegion} this
49260          * @param {Roo.ContentPanel} panel The panel
49261          */
49262         "panelremoved" : true,
49263         /**
49264          * @event collapsed
49265          * Fires when this region is collapsed.
49266          * @param {Roo.LayoutRegion} this
49267          */
49268         "collapsed" : true,
49269         /**
49270          * @event expanded
49271          * Fires when this region is expanded.
49272          * @param {Roo.LayoutRegion} this
49273          */
49274         "expanded" : true,
49275         /**
49276          * @event slideshow
49277          * Fires when this region is slid into view.
49278          * @param {Roo.LayoutRegion} this
49279          */
49280         "slideshow" : true,
49281         /**
49282          * @event slidehide
49283          * Fires when this region slides out of view. 
49284          * @param {Roo.LayoutRegion} this
49285          */
49286         "slidehide" : true,
49287         /**
49288          * @event panelactivated
49289          * Fires when a panel is activated. 
49290          * @param {Roo.LayoutRegion} this
49291          * @param {Roo.ContentPanel} panel The activated panel
49292          */
49293         "panelactivated" : true,
49294         /**
49295          * @event resized
49296          * Fires when the user resizes this region. 
49297          * @param {Roo.LayoutRegion} this
49298          * @param {Number} newSize The new size (width for east/west, height for north/south)
49299          */
49300         "resized" : true
49301     };
49302     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49303     this.panels = new Roo.util.MixedCollection();
49304     this.panels.getKey = this.getPanelId.createDelegate(this);
49305     this.box = null;
49306     this.activePanel = null;
49307     // ensure listeners are added...
49308     
49309     if (config.listeners || config.events) {
49310         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49311             listeners : config.listeners || {},
49312             events : config.events || {}
49313         });
49314     }
49315     
49316     if(skipConfig !== true){
49317         this.applyConfig(config);
49318     }
49319 };
49320
49321 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49322     getPanelId : function(p){
49323         return p.getId();
49324     },
49325     
49326     applyConfig : function(config){
49327         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49328         this.config = config;
49329         
49330     },
49331     
49332     /**
49333      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49334      * the width, for horizontal (north, south) the height.
49335      * @param {Number} newSize The new width or height
49336      */
49337     resizeTo : function(newSize){
49338         var el = this.el ? this.el :
49339                  (this.activePanel ? this.activePanel.getEl() : null);
49340         if(el){
49341             switch(this.position){
49342                 case "east":
49343                 case "west":
49344                     el.setWidth(newSize);
49345                     this.fireEvent("resized", this, newSize);
49346                 break;
49347                 case "north":
49348                 case "south":
49349                     el.setHeight(newSize);
49350                     this.fireEvent("resized", this, newSize);
49351                 break;                
49352             }
49353         }
49354     },
49355     
49356     getBox : function(){
49357         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49358     },
49359     
49360     getMargins : function(){
49361         return this.margins;
49362     },
49363     
49364     updateBox : function(box){
49365         this.box = box;
49366         var el = this.activePanel.getEl();
49367         el.dom.style.left = box.x + "px";
49368         el.dom.style.top = box.y + "px";
49369         this.activePanel.setSize(box.width, box.height);
49370     },
49371     
49372     /**
49373      * Returns the container element for this region.
49374      * @return {Roo.Element}
49375      */
49376     getEl : function(){
49377         return this.activePanel;
49378     },
49379     
49380     /**
49381      * Returns true if this region is currently visible.
49382      * @return {Boolean}
49383      */
49384     isVisible : function(){
49385         return this.activePanel ? true : false;
49386     },
49387     
49388     setActivePanel : function(panel){
49389         panel = this.getPanel(panel);
49390         if(this.activePanel && this.activePanel != panel){
49391             this.activePanel.setActiveState(false);
49392             this.activePanel.getEl().setLeftTop(-10000,-10000);
49393         }
49394         this.activePanel = panel;
49395         panel.setActiveState(true);
49396         if(this.box){
49397             panel.setSize(this.box.width, this.box.height);
49398         }
49399         this.fireEvent("panelactivated", this, panel);
49400         this.fireEvent("invalidated");
49401     },
49402     
49403     /**
49404      * Show the specified panel.
49405      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49406      * @return {Roo.ContentPanel} The shown panel or null
49407      */
49408     showPanel : function(panel){
49409         if(panel = this.getPanel(panel)){
49410             this.setActivePanel(panel);
49411         }
49412         return panel;
49413     },
49414     
49415     /**
49416      * Get the active panel for this region.
49417      * @return {Roo.ContentPanel} The active panel or null
49418      */
49419     getActivePanel : function(){
49420         return this.activePanel;
49421     },
49422     
49423     /**
49424      * Add the passed ContentPanel(s)
49425      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49426      * @return {Roo.ContentPanel} The panel added (if only one was added)
49427      */
49428     add : function(panel){
49429         if(arguments.length > 1){
49430             for(var i = 0, len = arguments.length; i < len; i++) {
49431                 this.add(arguments[i]);
49432             }
49433             return null;
49434         }
49435         if(this.hasPanel(panel)){
49436             this.showPanel(panel);
49437             return panel;
49438         }
49439         var el = panel.getEl();
49440         if(el.dom.parentNode != this.mgr.el.dom){
49441             this.mgr.el.dom.appendChild(el.dom);
49442         }
49443         if(panel.setRegion){
49444             panel.setRegion(this);
49445         }
49446         this.panels.add(panel);
49447         el.setStyle("position", "absolute");
49448         if(!panel.background){
49449             this.setActivePanel(panel);
49450             if(this.config.initialSize && this.panels.getCount()==1){
49451                 this.resizeTo(this.config.initialSize);
49452             }
49453         }
49454         this.fireEvent("paneladded", this, panel);
49455         return panel;
49456     },
49457     
49458     /**
49459      * Returns true if the panel is in this region.
49460      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49461      * @return {Boolean}
49462      */
49463     hasPanel : function(panel){
49464         if(typeof panel == "object"){ // must be panel obj
49465             panel = panel.getId();
49466         }
49467         return this.getPanel(panel) ? true : false;
49468     },
49469     
49470     /**
49471      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49472      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49473      * @param {Boolean} preservePanel Overrides the config preservePanel option
49474      * @return {Roo.ContentPanel} The panel that was removed
49475      */
49476     remove : function(panel, preservePanel){
49477         panel = this.getPanel(panel);
49478         if(!panel){
49479             return null;
49480         }
49481         var e = {};
49482         this.fireEvent("beforeremove", this, panel, e);
49483         if(e.cancel === true){
49484             return null;
49485         }
49486         var panelId = panel.getId();
49487         this.panels.removeKey(panelId);
49488         return panel;
49489     },
49490     
49491     /**
49492      * Returns the panel specified or null if it's not in this region.
49493      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49494      * @return {Roo.ContentPanel}
49495      */
49496     getPanel : function(id){
49497         if(typeof id == "object"){ // must be panel obj
49498             return id;
49499         }
49500         return this.panels.get(id);
49501     },
49502     
49503     /**
49504      * Returns this regions position (north/south/east/west/center).
49505      * @return {String} 
49506      */
49507     getPosition: function(){
49508         return this.position;    
49509     }
49510 });/*
49511  * Based on:
49512  * Ext JS Library 1.1.1
49513  * Copyright(c) 2006-2007, Ext JS, LLC.
49514  *
49515  * Originally Released Under LGPL - original licence link has changed is not relivant.
49516  *
49517  * Fork - LGPL
49518  * <script type="text/javascript">
49519  */
49520  
49521 /**
49522  * @class Roo.LayoutRegion
49523  * @extends Roo.BasicLayoutRegion
49524  * This class represents a region in a layout manager.
49525  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49526  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49527  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49528  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49529  * @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})
49530  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49531  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49532  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49533  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49534  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49535  * @cfg {String}    title           The title for the region (overrides panel titles)
49536  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49537  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49538  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49539  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49540  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49541  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49542  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49543  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49544  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49545  * @cfg {Boolean}   showPin         True to show a pin button
49546  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49547  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49548  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49549  * @cfg {Number}    width           For East/West panels
49550  * @cfg {Number}    height          For North/South panels
49551  * @cfg {Boolean}   split           To show the splitter
49552  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49553  */
49554 Roo.LayoutRegion = function(mgr, config, pos){
49555     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49556     var dh = Roo.DomHelper;
49557     /** This region's container element 
49558     * @type Roo.Element */
49559     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49560     /** This region's title element 
49561     * @type Roo.Element */
49562
49563     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49564         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49565         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49566     ]}, true);
49567     this.titleEl.enableDisplayMode();
49568     /** This region's title text element 
49569     * @type HTMLElement */
49570     this.titleTextEl = this.titleEl.dom.firstChild;
49571     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49572     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49573     this.closeBtn.enableDisplayMode();
49574     this.closeBtn.on("click", this.closeClicked, this);
49575     this.closeBtn.hide();
49576
49577     this.createBody(config);
49578     this.visible = true;
49579     this.collapsed = false;
49580
49581     if(config.hideWhenEmpty){
49582         this.hide();
49583         this.on("paneladded", this.validateVisibility, this);
49584         this.on("panelremoved", this.validateVisibility, this);
49585     }
49586     this.applyConfig(config);
49587 };
49588
49589 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49590
49591     createBody : function(){
49592         /** This region's body element 
49593         * @type Roo.Element */
49594         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49595     },
49596
49597     applyConfig : function(c){
49598         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49599             var dh = Roo.DomHelper;
49600             if(c.titlebar !== false){
49601                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49602                 this.collapseBtn.on("click", this.collapse, this);
49603                 this.collapseBtn.enableDisplayMode();
49604
49605                 if(c.showPin === true || this.showPin){
49606                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49607                     this.stickBtn.enableDisplayMode();
49608                     this.stickBtn.on("click", this.expand, this);
49609                     this.stickBtn.hide();
49610                 }
49611             }
49612             /** This region's collapsed element
49613             * @type Roo.Element */
49614             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49615                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49616             ]}, true);
49617             if(c.floatable !== false){
49618                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49619                this.collapsedEl.on("click", this.collapseClick, this);
49620             }
49621
49622             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49623                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49624                    id: "message", unselectable: "on", style:{"float":"left"}});
49625                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49626              }
49627             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49628             this.expandBtn.on("click", this.expand, this);
49629         }
49630         if(this.collapseBtn){
49631             this.collapseBtn.setVisible(c.collapsible == true);
49632         }
49633         this.cmargins = c.cmargins || this.cmargins ||
49634                          (this.position == "west" || this.position == "east" ?
49635                              {top: 0, left: 2, right:2, bottom: 0} :
49636                              {top: 2, left: 0, right:0, bottom: 2});
49637         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49638         this.bottomTabs = c.tabPosition != "top";
49639         this.autoScroll = c.autoScroll || false;
49640         if(this.autoScroll){
49641             this.bodyEl.setStyle("overflow", "auto");
49642         }else{
49643             this.bodyEl.setStyle("overflow", "hidden");
49644         }
49645         //if(c.titlebar !== false){
49646             if((!c.titlebar && !c.title) || c.titlebar === false){
49647                 this.titleEl.hide();
49648             }else{
49649                 this.titleEl.show();
49650                 if(c.title){
49651                     this.titleTextEl.innerHTML = c.title;
49652                 }
49653             }
49654         //}
49655         this.duration = c.duration || .30;
49656         this.slideDuration = c.slideDuration || .45;
49657         this.config = c;
49658         if(c.collapsed){
49659             this.collapse(true);
49660         }
49661         if(c.hidden){
49662             this.hide();
49663         }
49664     },
49665     /**
49666      * Returns true if this region is currently visible.
49667      * @return {Boolean}
49668      */
49669     isVisible : function(){
49670         return this.visible;
49671     },
49672
49673     /**
49674      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49675      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49676      */
49677     setCollapsedTitle : function(title){
49678         title = title || "&#160;";
49679         if(this.collapsedTitleTextEl){
49680             this.collapsedTitleTextEl.innerHTML = title;
49681         }
49682     },
49683
49684     getBox : function(){
49685         var b;
49686         if(!this.collapsed){
49687             b = this.el.getBox(false, true);
49688         }else{
49689             b = this.collapsedEl.getBox(false, true);
49690         }
49691         return b;
49692     },
49693
49694     getMargins : function(){
49695         return this.collapsed ? this.cmargins : this.margins;
49696     },
49697
49698     highlight : function(){
49699         this.el.addClass("x-layout-panel-dragover");
49700     },
49701
49702     unhighlight : function(){
49703         this.el.removeClass("x-layout-panel-dragover");
49704     },
49705
49706     updateBox : function(box){
49707         this.box = box;
49708         if(!this.collapsed){
49709             this.el.dom.style.left = box.x + "px";
49710             this.el.dom.style.top = box.y + "px";
49711             this.updateBody(box.width, box.height);
49712         }else{
49713             this.collapsedEl.dom.style.left = box.x + "px";
49714             this.collapsedEl.dom.style.top = box.y + "px";
49715             this.collapsedEl.setSize(box.width, box.height);
49716         }
49717         if(this.tabs){
49718             this.tabs.autoSizeTabs();
49719         }
49720     },
49721
49722     updateBody : function(w, h){
49723         if(w !== null){
49724             this.el.setWidth(w);
49725             w -= this.el.getBorderWidth("rl");
49726             if(this.config.adjustments){
49727                 w += this.config.adjustments[0];
49728             }
49729         }
49730         if(h !== null){
49731             this.el.setHeight(h);
49732             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49733             h -= this.el.getBorderWidth("tb");
49734             if(this.config.adjustments){
49735                 h += this.config.adjustments[1];
49736             }
49737             this.bodyEl.setHeight(h);
49738             if(this.tabs){
49739                 h = this.tabs.syncHeight(h);
49740             }
49741         }
49742         if(this.panelSize){
49743             w = w !== null ? w : this.panelSize.width;
49744             h = h !== null ? h : this.panelSize.height;
49745         }
49746         if(this.activePanel){
49747             var el = this.activePanel.getEl();
49748             w = w !== null ? w : el.getWidth();
49749             h = h !== null ? h : el.getHeight();
49750             this.panelSize = {width: w, height: h};
49751             this.activePanel.setSize(w, h);
49752         }
49753         if(Roo.isIE && this.tabs){
49754             this.tabs.el.repaint();
49755         }
49756     },
49757
49758     /**
49759      * Returns the container element for this region.
49760      * @return {Roo.Element}
49761      */
49762     getEl : function(){
49763         return this.el;
49764     },
49765
49766     /**
49767      * Hides this region.
49768      */
49769     hide : function(){
49770         if(!this.collapsed){
49771             this.el.dom.style.left = "-2000px";
49772             this.el.hide();
49773         }else{
49774             this.collapsedEl.dom.style.left = "-2000px";
49775             this.collapsedEl.hide();
49776         }
49777         this.visible = false;
49778         this.fireEvent("visibilitychange", this, false);
49779     },
49780
49781     /**
49782      * Shows this region if it was previously hidden.
49783      */
49784     show : function(){
49785         if(!this.collapsed){
49786             this.el.show();
49787         }else{
49788             this.collapsedEl.show();
49789         }
49790         this.visible = true;
49791         this.fireEvent("visibilitychange", this, true);
49792     },
49793
49794     closeClicked : function(){
49795         if(this.activePanel){
49796             this.remove(this.activePanel);
49797         }
49798     },
49799
49800     collapseClick : function(e){
49801         if(this.isSlid){
49802            e.stopPropagation();
49803            this.slideIn();
49804         }else{
49805            e.stopPropagation();
49806            this.slideOut();
49807         }
49808     },
49809
49810     /**
49811      * Collapses this region.
49812      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49813      */
49814     collapse : function(skipAnim){
49815         if(this.collapsed) return;
49816         this.collapsed = true;
49817         if(this.split){
49818             this.split.el.hide();
49819         }
49820         if(this.config.animate && skipAnim !== true){
49821             this.fireEvent("invalidated", this);
49822             this.animateCollapse();
49823         }else{
49824             this.el.setLocation(-20000,-20000);
49825             this.el.hide();
49826             this.collapsedEl.show();
49827             this.fireEvent("collapsed", this);
49828             this.fireEvent("invalidated", this);
49829         }
49830     },
49831
49832     animateCollapse : function(){
49833         // overridden
49834     },
49835
49836     /**
49837      * Expands this region if it was previously collapsed.
49838      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49839      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49840      */
49841     expand : function(e, skipAnim){
49842         if(e) e.stopPropagation();
49843         if(!this.collapsed || this.el.hasActiveFx()) return;
49844         if(this.isSlid){
49845             this.afterSlideIn();
49846             skipAnim = true;
49847         }
49848         this.collapsed = false;
49849         if(this.config.animate && skipAnim !== true){
49850             this.animateExpand();
49851         }else{
49852             this.el.show();
49853             if(this.split){
49854                 this.split.el.show();
49855             }
49856             this.collapsedEl.setLocation(-2000,-2000);
49857             this.collapsedEl.hide();
49858             this.fireEvent("invalidated", this);
49859             this.fireEvent("expanded", this);
49860         }
49861     },
49862
49863     animateExpand : function(){
49864         // overridden
49865     },
49866
49867     initTabs : function()
49868     {
49869         this.bodyEl.setStyle("overflow", "hidden");
49870         var ts = new Roo.TabPanel(
49871                 this.bodyEl.dom,
49872                 {
49873                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49874                     disableTooltips: this.config.disableTabTips,
49875                     toolbar : this.config.toolbar
49876                 }
49877         );
49878         if(this.config.hideTabs){
49879             ts.stripWrap.setDisplayed(false);
49880         }
49881         this.tabs = ts;
49882         ts.resizeTabs = this.config.resizeTabs === true;
49883         ts.minTabWidth = this.config.minTabWidth || 40;
49884         ts.maxTabWidth = this.config.maxTabWidth || 250;
49885         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49886         ts.monitorResize = false;
49887         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49888         ts.bodyEl.addClass('x-layout-tabs-body');
49889         this.panels.each(this.initPanelAsTab, this);
49890     },
49891
49892     initPanelAsTab : function(panel){
49893         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49894                     this.config.closeOnTab && panel.isClosable());
49895         if(panel.tabTip !== undefined){
49896             ti.setTooltip(panel.tabTip);
49897         }
49898         ti.on("activate", function(){
49899               this.setActivePanel(panel);
49900         }, this);
49901         if(this.config.closeOnTab){
49902             ti.on("beforeclose", function(t, e){
49903                 e.cancel = true;
49904                 this.remove(panel);
49905             }, this);
49906         }
49907         return ti;
49908     },
49909
49910     updatePanelTitle : function(panel, title){
49911         if(this.activePanel == panel){
49912             this.updateTitle(title);
49913         }
49914         if(this.tabs){
49915             var ti = this.tabs.getTab(panel.getEl().id);
49916             ti.setText(title);
49917             if(panel.tabTip !== undefined){
49918                 ti.setTooltip(panel.tabTip);
49919             }
49920         }
49921     },
49922
49923     updateTitle : function(title){
49924         if(this.titleTextEl && !this.config.title){
49925             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49926         }
49927     },
49928
49929     setActivePanel : function(panel){
49930         panel = this.getPanel(panel);
49931         if(this.activePanel && this.activePanel != panel){
49932             this.activePanel.setActiveState(false);
49933         }
49934         this.activePanel = panel;
49935         panel.setActiveState(true);
49936         if(this.panelSize){
49937             panel.setSize(this.panelSize.width, this.panelSize.height);
49938         }
49939         if(this.closeBtn){
49940             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49941         }
49942         this.updateTitle(panel.getTitle());
49943         if(this.tabs){
49944             this.fireEvent("invalidated", this);
49945         }
49946         this.fireEvent("panelactivated", this, panel);
49947     },
49948
49949     /**
49950      * Shows the specified panel.
49951      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49952      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49953      */
49954     showPanel : function(panel){
49955         if(panel = this.getPanel(panel)){
49956             if(this.tabs){
49957                 var tab = this.tabs.getTab(panel.getEl().id);
49958                 if(tab.isHidden()){
49959                     this.tabs.unhideTab(tab.id);
49960                 }
49961                 tab.activate();
49962             }else{
49963                 this.setActivePanel(panel);
49964             }
49965         }
49966         return panel;
49967     },
49968
49969     /**
49970      * Get the active panel for this region.
49971      * @return {Roo.ContentPanel} The active panel or null
49972      */
49973     getActivePanel : function(){
49974         return this.activePanel;
49975     },
49976
49977     validateVisibility : function(){
49978         if(this.panels.getCount() < 1){
49979             this.updateTitle("&#160;");
49980             this.closeBtn.hide();
49981             this.hide();
49982         }else{
49983             if(!this.isVisible()){
49984                 this.show();
49985             }
49986         }
49987     },
49988
49989     /**
49990      * Adds the passed ContentPanel(s) to this region.
49991      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49992      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49993      */
49994     add : function(panel){
49995         if(arguments.length > 1){
49996             for(var i = 0, len = arguments.length; i < len; i++) {
49997                 this.add(arguments[i]);
49998             }
49999             return null;
50000         }
50001         if(this.hasPanel(panel)){
50002             this.showPanel(panel);
50003             return panel;
50004         }
50005         panel.setRegion(this);
50006         this.panels.add(panel);
50007         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50008             this.bodyEl.dom.appendChild(panel.getEl().dom);
50009             if(panel.background !== true){
50010                 this.setActivePanel(panel);
50011             }
50012             this.fireEvent("paneladded", this, panel);
50013             return panel;
50014         }
50015         if(!this.tabs){
50016             this.initTabs();
50017         }else{
50018             this.initPanelAsTab(panel);
50019         }
50020         if(panel.background !== true){
50021             this.tabs.activate(panel.getEl().id);
50022         }
50023         this.fireEvent("paneladded", this, panel);
50024         return panel;
50025     },
50026
50027     /**
50028      * Hides the tab for the specified panel.
50029      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50030      */
50031     hidePanel : function(panel){
50032         if(this.tabs && (panel = this.getPanel(panel))){
50033             this.tabs.hideTab(panel.getEl().id);
50034         }
50035     },
50036
50037     /**
50038      * Unhides the tab for a previously hidden panel.
50039      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50040      */
50041     unhidePanel : function(panel){
50042         if(this.tabs && (panel = this.getPanel(panel))){
50043             this.tabs.unhideTab(panel.getEl().id);
50044         }
50045     },
50046
50047     clearPanels : function(){
50048         while(this.panels.getCount() > 0){
50049              this.remove(this.panels.first());
50050         }
50051     },
50052
50053     /**
50054      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50055      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50056      * @param {Boolean} preservePanel Overrides the config preservePanel option
50057      * @return {Roo.ContentPanel} The panel that was removed
50058      */
50059     remove : function(panel, preservePanel){
50060         panel = this.getPanel(panel);
50061         if(!panel){
50062             return null;
50063         }
50064         var e = {};
50065         this.fireEvent("beforeremove", this, panel, e);
50066         if(e.cancel === true){
50067             return null;
50068         }
50069         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50070         var panelId = panel.getId();
50071         this.panels.removeKey(panelId);
50072         if(preservePanel){
50073             document.body.appendChild(panel.getEl().dom);
50074         }
50075         if(this.tabs){
50076             this.tabs.removeTab(panel.getEl().id);
50077         }else if (!preservePanel){
50078             this.bodyEl.dom.removeChild(panel.getEl().dom);
50079         }
50080         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50081             var p = this.panels.first();
50082             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50083             tempEl.appendChild(p.getEl().dom);
50084             this.bodyEl.update("");
50085             this.bodyEl.dom.appendChild(p.getEl().dom);
50086             tempEl = null;
50087             this.updateTitle(p.getTitle());
50088             this.tabs = null;
50089             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50090             this.setActivePanel(p);
50091         }
50092         panel.setRegion(null);
50093         if(this.activePanel == panel){
50094             this.activePanel = null;
50095         }
50096         if(this.config.autoDestroy !== false && preservePanel !== true){
50097             try{panel.destroy();}catch(e){}
50098         }
50099         this.fireEvent("panelremoved", this, panel);
50100         return panel;
50101     },
50102
50103     /**
50104      * Returns the TabPanel component used by this region
50105      * @return {Roo.TabPanel}
50106      */
50107     getTabs : function(){
50108         return this.tabs;
50109     },
50110
50111     createTool : function(parentEl, className){
50112         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50113             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50114         btn.addClassOnOver("x-layout-tools-button-over");
50115         return btn;
50116     }
50117 });/*
50118  * Based on:
50119  * Ext JS Library 1.1.1
50120  * Copyright(c) 2006-2007, Ext JS, LLC.
50121  *
50122  * Originally Released Under LGPL - original licence link has changed is not relivant.
50123  *
50124  * Fork - LGPL
50125  * <script type="text/javascript">
50126  */
50127  
50128
50129
50130 /**
50131  * @class Roo.SplitLayoutRegion
50132  * @extends Roo.LayoutRegion
50133  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50134  */
50135 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50136     this.cursor = cursor;
50137     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50138 };
50139
50140 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50141     splitTip : "Drag to resize.",
50142     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50143     useSplitTips : false,
50144
50145     applyConfig : function(config){
50146         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50147         if(config.split){
50148             if(!this.split){
50149                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50150                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50151                 /** The SplitBar for this region 
50152                 * @type Roo.SplitBar */
50153                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50154                 this.split.on("moved", this.onSplitMove, this);
50155                 this.split.useShim = config.useShim === true;
50156                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50157                 if(this.useSplitTips){
50158                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50159                 }
50160                 if(config.collapsible){
50161                     this.split.el.on("dblclick", this.collapse,  this);
50162                 }
50163             }
50164             if(typeof config.minSize != "undefined"){
50165                 this.split.minSize = config.minSize;
50166             }
50167             if(typeof config.maxSize != "undefined"){
50168                 this.split.maxSize = config.maxSize;
50169             }
50170             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50171                 this.hideSplitter();
50172             }
50173         }
50174     },
50175
50176     getHMaxSize : function(){
50177          var cmax = this.config.maxSize || 10000;
50178          var center = this.mgr.getRegion("center");
50179          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50180     },
50181
50182     getVMaxSize : function(){
50183          var cmax = this.config.maxSize || 10000;
50184          var center = this.mgr.getRegion("center");
50185          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50186     },
50187
50188     onSplitMove : function(split, newSize){
50189         this.fireEvent("resized", this, newSize);
50190     },
50191     
50192     /** 
50193      * Returns the {@link Roo.SplitBar} for this region.
50194      * @return {Roo.SplitBar}
50195      */
50196     getSplitBar : function(){
50197         return this.split;
50198     },
50199     
50200     hide : function(){
50201         this.hideSplitter();
50202         Roo.SplitLayoutRegion.superclass.hide.call(this);
50203     },
50204
50205     hideSplitter : function(){
50206         if(this.split){
50207             this.split.el.setLocation(-2000,-2000);
50208             this.split.el.hide();
50209         }
50210     },
50211
50212     show : function(){
50213         if(this.split){
50214             this.split.el.show();
50215         }
50216         Roo.SplitLayoutRegion.superclass.show.call(this);
50217     },
50218     
50219     beforeSlide: function(){
50220         if(Roo.isGecko){// firefox overflow auto bug workaround
50221             this.bodyEl.clip();
50222             if(this.tabs) this.tabs.bodyEl.clip();
50223             if(this.activePanel){
50224                 this.activePanel.getEl().clip();
50225                 
50226                 if(this.activePanel.beforeSlide){
50227                     this.activePanel.beforeSlide();
50228                 }
50229             }
50230         }
50231     },
50232     
50233     afterSlide : function(){
50234         if(Roo.isGecko){// firefox overflow auto bug workaround
50235             this.bodyEl.unclip();
50236             if(this.tabs) this.tabs.bodyEl.unclip();
50237             if(this.activePanel){
50238                 this.activePanel.getEl().unclip();
50239                 if(this.activePanel.afterSlide){
50240                     this.activePanel.afterSlide();
50241                 }
50242             }
50243         }
50244     },
50245
50246     initAutoHide : function(){
50247         if(this.autoHide !== false){
50248             if(!this.autoHideHd){
50249                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50250                 this.autoHideHd = {
50251                     "mouseout": function(e){
50252                         if(!e.within(this.el, true)){
50253                             st.delay(500);
50254                         }
50255                     },
50256                     "mouseover" : function(e){
50257                         st.cancel();
50258                     },
50259                     scope : this
50260                 };
50261             }
50262             this.el.on(this.autoHideHd);
50263         }
50264     },
50265
50266     clearAutoHide : function(){
50267         if(this.autoHide !== false){
50268             this.el.un("mouseout", this.autoHideHd.mouseout);
50269             this.el.un("mouseover", this.autoHideHd.mouseover);
50270         }
50271     },
50272
50273     clearMonitor : function(){
50274         Roo.get(document).un("click", this.slideInIf, this);
50275     },
50276
50277     // these names are backwards but not changed for compat
50278     slideOut : function(){
50279         if(this.isSlid || this.el.hasActiveFx()){
50280             return;
50281         }
50282         this.isSlid = true;
50283         if(this.collapseBtn){
50284             this.collapseBtn.hide();
50285         }
50286         this.closeBtnState = this.closeBtn.getStyle('display');
50287         this.closeBtn.hide();
50288         if(this.stickBtn){
50289             this.stickBtn.show();
50290         }
50291         this.el.show();
50292         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50293         this.beforeSlide();
50294         this.el.setStyle("z-index", 10001);
50295         this.el.slideIn(this.getSlideAnchor(), {
50296             callback: function(){
50297                 this.afterSlide();
50298                 this.initAutoHide();
50299                 Roo.get(document).on("click", this.slideInIf, this);
50300                 this.fireEvent("slideshow", this);
50301             },
50302             scope: this,
50303             block: true
50304         });
50305     },
50306
50307     afterSlideIn : function(){
50308         this.clearAutoHide();
50309         this.isSlid = false;
50310         this.clearMonitor();
50311         this.el.setStyle("z-index", "");
50312         if(this.collapseBtn){
50313             this.collapseBtn.show();
50314         }
50315         this.closeBtn.setStyle('display', this.closeBtnState);
50316         if(this.stickBtn){
50317             this.stickBtn.hide();
50318         }
50319         this.fireEvent("slidehide", this);
50320     },
50321
50322     slideIn : function(cb){
50323         if(!this.isSlid || this.el.hasActiveFx()){
50324             Roo.callback(cb);
50325             return;
50326         }
50327         this.isSlid = false;
50328         this.beforeSlide();
50329         this.el.slideOut(this.getSlideAnchor(), {
50330             callback: function(){
50331                 this.el.setLeftTop(-10000, -10000);
50332                 this.afterSlide();
50333                 this.afterSlideIn();
50334                 Roo.callback(cb);
50335             },
50336             scope: this,
50337             block: true
50338         });
50339     },
50340     
50341     slideInIf : function(e){
50342         if(!e.within(this.el)){
50343             this.slideIn();
50344         }
50345     },
50346
50347     animateCollapse : function(){
50348         this.beforeSlide();
50349         this.el.setStyle("z-index", 20000);
50350         var anchor = this.getSlideAnchor();
50351         this.el.slideOut(anchor, {
50352             callback : function(){
50353                 this.el.setStyle("z-index", "");
50354                 this.collapsedEl.slideIn(anchor, {duration:.3});
50355                 this.afterSlide();
50356                 this.el.setLocation(-10000,-10000);
50357                 this.el.hide();
50358                 this.fireEvent("collapsed", this);
50359             },
50360             scope: this,
50361             block: true
50362         });
50363     },
50364
50365     animateExpand : function(){
50366         this.beforeSlide();
50367         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50368         this.el.setStyle("z-index", 20000);
50369         this.collapsedEl.hide({
50370             duration:.1
50371         });
50372         this.el.slideIn(this.getSlideAnchor(), {
50373             callback : function(){
50374                 this.el.setStyle("z-index", "");
50375                 this.afterSlide();
50376                 if(this.split){
50377                     this.split.el.show();
50378                 }
50379                 this.fireEvent("invalidated", this);
50380                 this.fireEvent("expanded", this);
50381             },
50382             scope: this,
50383             block: true
50384         });
50385     },
50386
50387     anchors : {
50388         "west" : "left",
50389         "east" : "right",
50390         "north" : "top",
50391         "south" : "bottom"
50392     },
50393
50394     sanchors : {
50395         "west" : "l",
50396         "east" : "r",
50397         "north" : "t",
50398         "south" : "b"
50399     },
50400
50401     canchors : {
50402         "west" : "tl-tr",
50403         "east" : "tr-tl",
50404         "north" : "tl-bl",
50405         "south" : "bl-tl"
50406     },
50407
50408     getAnchor : function(){
50409         return this.anchors[this.position];
50410     },
50411
50412     getCollapseAnchor : function(){
50413         return this.canchors[this.position];
50414     },
50415
50416     getSlideAnchor : function(){
50417         return this.sanchors[this.position];
50418     },
50419
50420     getAlignAdj : function(){
50421         var cm = this.cmargins;
50422         switch(this.position){
50423             case "west":
50424                 return [0, 0];
50425             break;
50426             case "east":
50427                 return [0, 0];
50428             break;
50429             case "north":
50430                 return [0, 0];
50431             break;
50432             case "south":
50433                 return [0, 0];
50434             break;
50435         }
50436     },
50437
50438     getExpandAdj : function(){
50439         var c = this.collapsedEl, cm = this.cmargins;
50440         switch(this.position){
50441             case "west":
50442                 return [-(cm.right+c.getWidth()+cm.left), 0];
50443             break;
50444             case "east":
50445                 return [cm.right+c.getWidth()+cm.left, 0];
50446             break;
50447             case "north":
50448                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50449             break;
50450             case "south":
50451                 return [0, cm.top+cm.bottom+c.getHeight()];
50452             break;
50453         }
50454     }
50455 });/*
50456  * Based on:
50457  * Ext JS Library 1.1.1
50458  * Copyright(c) 2006-2007, Ext JS, LLC.
50459  *
50460  * Originally Released Under LGPL - original licence link has changed is not relivant.
50461  *
50462  * Fork - LGPL
50463  * <script type="text/javascript">
50464  */
50465 /*
50466  * These classes are private internal classes
50467  */
50468 Roo.CenterLayoutRegion = function(mgr, config){
50469     Roo.LayoutRegion.call(this, mgr, config, "center");
50470     this.visible = true;
50471     this.minWidth = config.minWidth || 20;
50472     this.minHeight = config.minHeight || 20;
50473 };
50474
50475 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50476     hide : function(){
50477         // center panel can't be hidden
50478     },
50479     
50480     show : function(){
50481         // center panel can't be hidden
50482     },
50483     
50484     getMinWidth: function(){
50485         return this.minWidth;
50486     },
50487     
50488     getMinHeight: function(){
50489         return this.minHeight;
50490     }
50491 });
50492
50493
50494 Roo.NorthLayoutRegion = function(mgr, config){
50495     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50496     if(this.split){
50497         this.split.placement = Roo.SplitBar.TOP;
50498         this.split.orientation = Roo.SplitBar.VERTICAL;
50499         this.split.el.addClass("x-layout-split-v");
50500     }
50501     var size = config.initialSize || config.height;
50502     if(typeof size != "undefined"){
50503         this.el.setHeight(size);
50504     }
50505 };
50506 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50507     orientation: Roo.SplitBar.VERTICAL,
50508     getBox : function(){
50509         if(this.collapsed){
50510             return this.collapsedEl.getBox();
50511         }
50512         var box = this.el.getBox();
50513         if(this.split){
50514             box.height += this.split.el.getHeight();
50515         }
50516         return box;
50517     },
50518     
50519     updateBox : function(box){
50520         if(this.split && !this.collapsed){
50521             box.height -= this.split.el.getHeight();
50522             this.split.el.setLeft(box.x);
50523             this.split.el.setTop(box.y+box.height);
50524             this.split.el.setWidth(box.width);
50525         }
50526         if(this.collapsed){
50527             this.updateBody(box.width, null);
50528         }
50529         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50530     }
50531 });
50532
50533 Roo.SouthLayoutRegion = function(mgr, config){
50534     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50535     if(this.split){
50536         this.split.placement = Roo.SplitBar.BOTTOM;
50537         this.split.orientation = Roo.SplitBar.VERTICAL;
50538         this.split.el.addClass("x-layout-split-v");
50539     }
50540     var size = config.initialSize || config.height;
50541     if(typeof size != "undefined"){
50542         this.el.setHeight(size);
50543     }
50544 };
50545 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50546     orientation: Roo.SplitBar.VERTICAL,
50547     getBox : function(){
50548         if(this.collapsed){
50549             return this.collapsedEl.getBox();
50550         }
50551         var box = this.el.getBox();
50552         if(this.split){
50553             var sh = this.split.el.getHeight();
50554             box.height += sh;
50555             box.y -= sh;
50556         }
50557         return box;
50558     },
50559     
50560     updateBox : function(box){
50561         if(this.split && !this.collapsed){
50562             var sh = this.split.el.getHeight();
50563             box.height -= sh;
50564             box.y += sh;
50565             this.split.el.setLeft(box.x);
50566             this.split.el.setTop(box.y-sh);
50567             this.split.el.setWidth(box.width);
50568         }
50569         if(this.collapsed){
50570             this.updateBody(box.width, null);
50571         }
50572         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50573     }
50574 });
50575
50576 Roo.EastLayoutRegion = function(mgr, config){
50577     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50578     if(this.split){
50579         this.split.placement = Roo.SplitBar.RIGHT;
50580         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50581         this.split.el.addClass("x-layout-split-h");
50582     }
50583     var size = config.initialSize || config.width;
50584     if(typeof size != "undefined"){
50585         this.el.setWidth(size);
50586     }
50587 };
50588 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50589     orientation: Roo.SplitBar.HORIZONTAL,
50590     getBox : function(){
50591         if(this.collapsed){
50592             return this.collapsedEl.getBox();
50593         }
50594         var box = this.el.getBox();
50595         if(this.split){
50596             var sw = this.split.el.getWidth();
50597             box.width += sw;
50598             box.x -= sw;
50599         }
50600         return box;
50601     },
50602
50603     updateBox : function(box){
50604         if(this.split && !this.collapsed){
50605             var sw = this.split.el.getWidth();
50606             box.width -= sw;
50607             this.split.el.setLeft(box.x);
50608             this.split.el.setTop(box.y);
50609             this.split.el.setHeight(box.height);
50610             box.x += sw;
50611         }
50612         if(this.collapsed){
50613             this.updateBody(null, box.height);
50614         }
50615         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50616     }
50617 });
50618
50619 Roo.WestLayoutRegion = function(mgr, config){
50620     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50621     if(this.split){
50622         this.split.placement = Roo.SplitBar.LEFT;
50623         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50624         this.split.el.addClass("x-layout-split-h");
50625     }
50626     var size = config.initialSize || config.width;
50627     if(typeof size != "undefined"){
50628         this.el.setWidth(size);
50629     }
50630 };
50631 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50632     orientation: Roo.SplitBar.HORIZONTAL,
50633     getBox : function(){
50634         if(this.collapsed){
50635             return this.collapsedEl.getBox();
50636         }
50637         var box = this.el.getBox();
50638         if(this.split){
50639             box.width += this.split.el.getWidth();
50640         }
50641         return box;
50642     },
50643     
50644     updateBox : function(box){
50645         if(this.split && !this.collapsed){
50646             var sw = this.split.el.getWidth();
50647             box.width -= sw;
50648             this.split.el.setLeft(box.x+box.width);
50649             this.split.el.setTop(box.y);
50650             this.split.el.setHeight(box.height);
50651         }
50652         if(this.collapsed){
50653             this.updateBody(null, box.height);
50654         }
50655         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50656     }
50657 });
50658 /*
50659  * Based on:
50660  * Ext JS Library 1.1.1
50661  * Copyright(c) 2006-2007, Ext JS, LLC.
50662  *
50663  * Originally Released Under LGPL - original licence link has changed is not relivant.
50664  *
50665  * Fork - LGPL
50666  * <script type="text/javascript">
50667  */
50668  
50669  
50670 /*
50671  * Private internal class for reading and applying state
50672  */
50673 Roo.LayoutStateManager = function(layout){
50674      // default empty state
50675      this.state = {
50676         north: {},
50677         south: {},
50678         east: {},
50679         west: {}       
50680     };
50681 };
50682
50683 Roo.LayoutStateManager.prototype = {
50684     init : function(layout, provider){
50685         this.provider = provider;
50686         var state = provider.get(layout.id+"-layout-state");
50687         if(state){
50688             var wasUpdating = layout.isUpdating();
50689             if(!wasUpdating){
50690                 layout.beginUpdate();
50691             }
50692             for(var key in state){
50693                 if(typeof state[key] != "function"){
50694                     var rstate = state[key];
50695                     var r = layout.getRegion(key);
50696                     if(r && rstate){
50697                         if(rstate.size){
50698                             r.resizeTo(rstate.size);
50699                         }
50700                         if(rstate.collapsed == true){
50701                             r.collapse(true);
50702                         }else{
50703                             r.expand(null, true);
50704                         }
50705                     }
50706                 }
50707             }
50708             if(!wasUpdating){
50709                 layout.endUpdate();
50710             }
50711             this.state = state; 
50712         }
50713         this.layout = layout;
50714         layout.on("regionresized", this.onRegionResized, this);
50715         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50716         layout.on("regionexpanded", this.onRegionExpanded, this);
50717     },
50718     
50719     storeState : function(){
50720         this.provider.set(this.layout.id+"-layout-state", this.state);
50721     },
50722     
50723     onRegionResized : function(region, newSize){
50724         this.state[region.getPosition()].size = newSize;
50725         this.storeState();
50726     },
50727     
50728     onRegionCollapsed : function(region){
50729         this.state[region.getPosition()].collapsed = true;
50730         this.storeState();
50731     },
50732     
50733     onRegionExpanded : function(region){
50734         this.state[region.getPosition()].collapsed = false;
50735         this.storeState();
50736     }
50737 };/*
50738  * Based on:
50739  * Ext JS Library 1.1.1
50740  * Copyright(c) 2006-2007, Ext JS, LLC.
50741  *
50742  * Originally Released Under LGPL - original licence link has changed is not relivant.
50743  *
50744  * Fork - LGPL
50745  * <script type="text/javascript">
50746  */
50747 /**
50748  * @class Roo.ContentPanel
50749  * @extends Roo.util.Observable
50750  * A basic ContentPanel element.
50751  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50752  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50753  * @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
50754  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50755  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50756  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50757  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50758  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50759  * @cfg {String} title          The title for this panel
50760  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50761  * @cfg {String} url            Calls {@link #setUrl} with this value
50762  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50763  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50764  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50765  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50766
50767  * @constructor
50768  * Create a new ContentPanel.
50769  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50770  * @param {String/Object} config A string to set only the title or a config object
50771  * @param {String} content (optional) Set the HTML content for this panel
50772  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50773  */
50774 Roo.ContentPanel = function(el, config, content){
50775     
50776      
50777     /*
50778     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50779         config = el;
50780         el = Roo.id();
50781     }
50782     if (config && config.parentLayout) { 
50783         el = config.parentLayout.el.createChild(); 
50784     }
50785     */
50786     if(el.autoCreate){ // xtype is available if this is called from factory
50787         config = el;
50788         el = Roo.id();
50789     }
50790     this.el = Roo.get(el);
50791     if(!this.el && config && config.autoCreate){
50792         if(typeof config.autoCreate == "object"){
50793             if(!config.autoCreate.id){
50794                 config.autoCreate.id = config.id||el;
50795             }
50796             this.el = Roo.DomHelper.append(document.body,
50797                         config.autoCreate, true);
50798         }else{
50799             this.el = Roo.DomHelper.append(document.body,
50800                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50801         }
50802     }
50803     this.closable = false;
50804     this.loaded = false;
50805     this.active = false;
50806     if(typeof config == "string"){
50807         this.title = config;
50808     }else{
50809         Roo.apply(this, config);
50810     }
50811     
50812     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50813         this.wrapEl = this.el.wrap();
50814         this.toolbar.container = this.el.insertSibling(false, 'before');
50815         this.toolbar = new Roo.Toolbar(this.toolbar);
50816     }
50817     
50818     // xtype created footer. - not sure if will work as we normally have to render first..
50819     if (this.footer && !this.footer.el && this.footer.xtype) {
50820         if (!this.wrapEl) {
50821             this.wrapEl = this.el.wrap();
50822         }
50823     
50824         this.footer.container = this.wrapEl.createChild();
50825          
50826         this.footer = Roo.factory(this.footer, Roo);
50827         
50828     }
50829     
50830     if(this.resizeEl){
50831         this.resizeEl = Roo.get(this.resizeEl, true);
50832     }else{
50833         this.resizeEl = this.el;
50834     }
50835     // handle view.xtype
50836     
50837  
50838     
50839     
50840     this.addEvents({
50841         /**
50842          * @event activate
50843          * Fires when this panel is activated. 
50844          * @param {Roo.ContentPanel} this
50845          */
50846         "activate" : true,
50847         /**
50848          * @event deactivate
50849          * Fires when this panel is activated. 
50850          * @param {Roo.ContentPanel} this
50851          */
50852         "deactivate" : true,
50853
50854         /**
50855          * @event resize
50856          * Fires when this panel is resized if fitToFrame is true.
50857          * @param {Roo.ContentPanel} this
50858          * @param {Number} width The width after any component adjustments
50859          * @param {Number} height The height after any component adjustments
50860          */
50861         "resize" : true,
50862         
50863          /**
50864          * @event render
50865          * Fires when this tab is created
50866          * @param {Roo.ContentPanel} this
50867          */
50868         "render" : true
50869         
50870         
50871         
50872     });
50873     
50874
50875     
50876     
50877     if(this.autoScroll){
50878         this.resizeEl.setStyle("overflow", "auto");
50879     } else {
50880         // fix randome scrolling
50881         this.el.on('scroll', function() {
50882             Roo.log('fix random scolling');
50883             this.scrollTo('top',0); 
50884         });
50885     }
50886     content = content || this.content;
50887     if(content){
50888         this.setContent(content);
50889     }
50890     if(config && config.url){
50891         this.setUrl(this.url, this.params, this.loadOnce);
50892     }
50893     
50894     
50895     
50896     Roo.ContentPanel.superclass.constructor.call(this);
50897     
50898     if (this.view && typeof(this.view.xtype) != 'undefined') {
50899         this.view.el = this.el.appendChild(document.createElement("div"));
50900         this.view = Roo.factory(this.view); 
50901         this.view.render  &&  this.view.render(false, '');  
50902     }
50903     
50904     
50905     this.fireEvent('render', this);
50906 };
50907
50908 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50909     tabTip:'',
50910     setRegion : function(region){
50911         this.region = region;
50912         if(region){
50913            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50914         }else{
50915            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50916         } 
50917     },
50918     
50919     /**
50920      * Returns the toolbar for this Panel if one was configured. 
50921      * @return {Roo.Toolbar} 
50922      */
50923     getToolbar : function(){
50924         return this.toolbar;
50925     },
50926     
50927     setActiveState : function(active){
50928         this.active = active;
50929         if(!active){
50930             this.fireEvent("deactivate", this);
50931         }else{
50932             this.fireEvent("activate", this);
50933         }
50934     },
50935     /**
50936      * Updates this panel's element
50937      * @param {String} content The new content
50938      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50939     */
50940     setContent : function(content, loadScripts){
50941         this.el.update(content, loadScripts);
50942     },
50943
50944     ignoreResize : function(w, h){
50945         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50946             return true;
50947         }else{
50948             this.lastSize = {width: w, height: h};
50949             return false;
50950         }
50951     },
50952     /**
50953      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50954      * @return {Roo.UpdateManager} The UpdateManager
50955      */
50956     getUpdateManager : function(){
50957         return this.el.getUpdateManager();
50958     },
50959      /**
50960      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50961      * @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:
50962 <pre><code>
50963 panel.load({
50964     url: "your-url.php",
50965     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50966     callback: yourFunction,
50967     scope: yourObject, //(optional scope)
50968     discardUrl: false,
50969     nocache: false,
50970     text: "Loading...",
50971     timeout: 30,
50972     scripts: false
50973 });
50974 </code></pre>
50975      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50976      * 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.
50977      * @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}
50978      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50979      * @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.
50980      * @return {Roo.ContentPanel} this
50981      */
50982     load : function(){
50983         var um = this.el.getUpdateManager();
50984         um.update.apply(um, arguments);
50985         return this;
50986     },
50987
50988
50989     /**
50990      * 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.
50991      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50992      * @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)
50993      * @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)
50994      * @return {Roo.UpdateManager} The UpdateManager
50995      */
50996     setUrl : function(url, params, loadOnce){
50997         if(this.refreshDelegate){
50998             this.removeListener("activate", this.refreshDelegate);
50999         }
51000         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51001         this.on("activate", this.refreshDelegate);
51002         return this.el.getUpdateManager();
51003     },
51004     
51005     _handleRefresh : function(url, params, loadOnce){
51006         if(!loadOnce || !this.loaded){
51007             var updater = this.el.getUpdateManager();
51008             updater.update(url, params, this._setLoaded.createDelegate(this));
51009         }
51010     },
51011     
51012     _setLoaded : function(){
51013         this.loaded = true;
51014     }, 
51015     
51016     /**
51017      * Returns this panel's id
51018      * @return {String} 
51019      */
51020     getId : function(){
51021         return this.el.id;
51022     },
51023     
51024     /** 
51025      * Returns this panel's element - used by regiosn to add.
51026      * @return {Roo.Element} 
51027      */
51028     getEl : function(){
51029         return this.wrapEl || this.el;
51030     },
51031     
51032     adjustForComponents : function(width, height)
51033     {
51034         //Roo.log('adjustForComponents ');
51035         if(this.resizeEl != this.el){
51036             width -= this.el.getFrameWidth('lr');
51037             height -= this.el.getFrameWidth('tb');
51038         }
51039         if(this.toolbar){
51040             var te = this.toolbar.getEl();
51041             height -= te.getHeight();
51042             te.setWidth(width);
51043         }
51044         if(this.footer){
51045             var te = this.footer.getEl();
51046             Roo.log("footer:" + te.getHeight());
51047             
51048             height -= te.getHeight();
51049             te.setWidth(width);
51050         }
51051         
51052         
51053         if(this.adjustments){
51054             width += this.adjustments[0];
51055             height += this.adjustments[1];
51056         }
51057         return {"width": width, "height": height};
51058     },
51059     
51060     setSize : function(width, height){
51061         if(this.fitToFrame && !this.ignoreResize(width, height)){
51062             if(this.fitContainer && this.resizeEl != this.el){
51063                 this.el.setSize(width, height);
51064             }
51065             var size = this.adjustForComponents(width, height);
51066             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51067             this.fireEvent('resize', this, size.width, size.height);
51068         }
51069     },
51070     
51071     /**
51072      * Returns this panel's title
51073      * @return {String} 
51074      */
51075     getTitle : function(){
51076         return this.title;
51077     },
51078     
51079     /**
51080      * Set this panel's title
51081      * @param {String} title
51082      */
51083     setTitle : function(title){
51084         this.title = title;
51085         if(this.region){
51086             this.region.updatePanelTitle(this, title);
51087         }
51088     },
51089     
51090     /**
51091      * Returns true is this panel was configured to be closable
51092      * @return {Boolean} 
51093      */
51094     isClosable : function(){
51095         return this.closable;
51096     },
51097     
51098     beforeSlide : function(){
51099         this.el.clip();
51100         this.resizeEl.clip();
51101     },
51102     
51103     afterSlide : function(){
51104         this.el.unclip();
51105         this.resizeEl.unclip();
51106     },
51107     
51108     /**
51109      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51110      *   Will fail silently if the {@link #setUrl} method has not been called.
51111      *   This does not activate the panel, just updates its content.
51112      */
51113     refresh : function(){
51114         if(this.refreshDelegate){
51115            this.loaded = false;
51116            this.refreshDelegate();
51117         }
51118     },
51119     
51120     /**
51121      * Destroys this panel
51122      */
51123     destroy : function(){
51124         this.el.removeAllListeners();
51125         var tempEl = document.createElement("span");
51126         tempEl.appendChild(this.el.dom);
51127         tempEl.innerHTML = "";
51128         this.el.remove();
51129         this.el = null;
51130     },
51131     
51132     /**
51133      * form - if the content panel contains a form - this is a reference to it.
51134      * @type {Roo.form.Form}
51135      */
51136     form : false,
51137     /**
51138      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51139      *    This contains a reference to it.
51140      * @type {Roo.View}
51141      */
51142     view : false,
51143     
51144       /**
51145      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51146      * <pre><code>
51147
51148 layout.addxtype({
51149        xtype : 'Form',
51150        items: [ .... ]
51151    }
51152 );
51153
51154 </code></pre>
51155      * @param {Object} cfg Xtype definition of item to add.
51156      */
51157     
51158     addxtype : function(cfg) {
51159         // add form..
51160         if (cfg.xtype.match(/^Form$/)) {
51161             
51162             var el;
51163             //if (this.footer) {
51164             //    el = this.footer.container.insertSibling(false, 'before');
51165             //} else {
51166                 el = this.el.createChild();
51167             //}
51168
51169             this.form = new  Roo.form.Form(cfg);
51170             
51171             
51172             if ( this.form.allItems.length) this.form.render(el.dom);
51173             return this.form;
51174         }
51175         // should only have one of theses..
51176         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51177             // views.. should not be just added - used named prop 'view''
51178             
51179             cfg.el = this.el.appendChild(document.createElement("div"));
51180             // factory?
51181             
51182             var ret = new Roo.factory(cfg);
51183              
51184              ret.render && ret.render(false, ''); // render blank..
51185             this.view = ret;
51186             return ret;
51187         }
51188         return false;
51189     }
51190 });
51191
51192 /**
51193  * @class Roo.GridPanel
51194  * @extends Roo.ContentPanel
51195  * @constructor
51196  * Create a new GridPanel.
51197  * @param {Roo.grid.Grid} grid The grid for this panel
51198  * @param {String/Object} config A string to set only the panel's title, or a config object
51199  */
51200 Roo.GridPanel = function(grid, config){
51201     
51202   
51203     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51204         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51205         
51206     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51207     
51208     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51209     
51210     if(this.toolbar){
51211         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51212     }
51213     // xtype created footer. - not sure if will work as we normally have to render first..
51214     if (this.footer && !this.footer.el && this.footer.xtype) {
51215         
51216         this.footer.container = this.grid.getView().getFooterPanel(true);
51217         this.footer.dataSource = this.grid.dataSource;
51218         this.footer = Roo.factory(this.footer, Roo);
51219         
51220     }
51221     
51222     grid.monitorWindowResize = false; // turn off autosizing
51223     grid.autoHeight = false;
51224     grid.autoWidth = false;
51225     this.grid = grid;
51226     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51227 };
51228
51229 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51230     getId : function(){
51231         return this.grid.id;
51232     },
51233     
51234     /**
51235      * Returns the grid for this panel
51236      * @return {Roo.grid.Grid} 
51237      */
51238     getGrid : function(){
51239         return this.grid;    
51240     },
51241     
51242     setSize : function(width, height){
51243         if(!this.ignoreResize(width, height)){
51244             var grid = this.grid;
51245             var size = this.adjustForComponents(width, height);
51246             grid.getGridEl().setSize(size.width, size.height);
51247             grid.autoSize();
51248         }
51249     },
51250     
51251     beforeSlide : function(){
51252         this.grid.getView().scroller.clip();
51253     },
51254     
51255     afterSlide : function(){
51256         this.grid.getView().scroller.unclip();
51257     },
51258     
51259     destroy : function(){
51260         this.grid.destroy();
51261         delete this.grid;
51262         Roo.GridPanel.superclass.destroy.call(this); 
51263     }
51264 });
51265
51266
51267 /**
51268  * @class Roo.NestedLayoutPanel
51269  * @extends Roo.ContentPanel
51270  * @constructor
51271  * Create a new NestedLayoutPanel.
51272  * 
51273  * 
51274  * @param {Roo.BorderLayout} layout The layout for this panel
51275  * @param {String/Object} config A string to set only the title or a config object
51276  */
51277 Roo.NestedLayoutPanel = function(layout, config)
51278 {
51279     // construct with only one argument..
51280     /* FIXME - implement nicer consturctors
51281     if (layout.layout) {
51282         config = layout;
51283         layout = config.layout;
51284         delete config.layout;
51285     }
51286     if (layout.xtype && !layout.getEl) {
51287         // then layout needs constructing..
51288         layout = Roo.factory(layout, Roo);
51289     }
51290     */
51291     
51292     
51293     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51294     
51295     layout.monitorWindowResize = false; // turn off autosizing
51296     this.layout = layout;
51297     this.layout.getEl().addClass("x-layout-nested-layout");
51298     
51299     
51300     
51301     
51302 };
51303
51304 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51305
51306     setSize : function(width, height){
51307         if(!this.ignoreResize(width, height)){
51308             var size = this.adjustForComponents(width, height);
51309             var el = this.layout.getEl();
51310             el.setSize(size.width, size.height);
51311             var touch = el.dom.offsetWidth;
51312             this.layout.layout();
51313             // ie requires a double layout on the first pass
51314             if(Roo.isIE && !this.initialized){
51315                 this.initialized = true;
51316                 this.layout.layout();
51317             }
51318         }
51319     },
51320     
51321     // activate all subpanels if not currently active..
51322     
51323     setActiveState : function(active){
51324         this.active = active;
51325         if(!active){
51326             this.fireEvent("deactivate", this);
51327             return;
51328         }
51329         
51330         this.fireEvent("activate", this);
51331         // not sure if this should happen before or after..
51332         if (!this.layout) {
51333             return; // should not happen..
51334         }
51335         var reg = false;
51336         for (var r in this.layout.regions) {
51337             reg = this.layout.getRegion(r);
51338             if (reg.getActivePanel()) {
51339                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51340                 reg.setActivePanel(reg.getActivePanel());
51341                 continue;
51342             }
51343             if (!reg.panels.length) {
51344                 continue;
51345             }
51346             reg.showPanel(reg.getPanel(0));
51347         }
51348         
51349         
51350         
51351         
51352     },
51353     
51354     /**
51355      * Returns the nested BorderLayout for this panel
51356      * @return {Roo.BorderLayout} 
51357      */
51358     getLayout : function(){
51359         return this.layout;
51360     },
51361     
51362      /**
51363      * Adds a xtype elements to the layout of the nested panel
51364      * <pre><code>
51365
51366 panel.addxtype({
51367        xtype : 'ContentPanel',
51368        region: 'west',
51369        items: [ .... ]
51370    }
51371 );
51372
51373 panel.addxtype({
51374         xtype : 'NestedLayoutPanel',
51375         region: 'west',
51376         layout: {
51377            center: { },
51378            west: { }   
51379         },
51380         items : [ ... list of content panels or nested layout panels.. ]
51381    }
51382 );
51383 </code></pre>
51384      * @param {Object} cfg Xtype definition of item to add.
51385      */
51386     addxtype : function(cfg) {
51387         return this.layout.addxtype(cfg);
51388     
51389     }
51390 });
51391
51392 Roo.ScrollPanel = function(el, config, content){
51393     config = config || {};
51394     config.fitToFrame = true;
51395     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51396     
51397     this.el.dom.style.overflow = "hidden";
51398     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51399     this.el.removeClass("x-layout-inactive-content");
51400     this.el.on("mousewheel", this.onWheel, this);
51401
51402     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51403     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51404     up.unselectable(); down.unselectable();
51405     up.on("click", this.scrollUp, this);
51406     down.on("click", this.scrollDown, this);
51407     up.addClassOnOver("x-scroller-btn-over");
51408     down.addClassOnOver("x-scroller-btn-over");
51409     up.addClassOnClick("x-scroller-btn-click");
51410     down.addClassOnClick("x-scroller-btn-click");
51411     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51412
51413     this.resizeEl = this.el;
51414     this.el = wrap; this.up = up; this.down = down;
51415 };
51416
51417 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51418     increment : 100,
51419     wheelIncrement : 5,
51420     scrollUp : function(){
51421         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51422     },
51423
51424     scrollDown : function(){
51425         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51426     },
51427
51428     afterScroll : function(){
51429         var el = this.resizeEl;
51430         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51431         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51432         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51433     },
51434
51435     setSize : function(){
51436         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51437         this.afterScroll();
51438     },
51439
51440     onWheel : function(e){
51441         var d = e.getWheelDelta();
51442         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51443         this.afterScroll();
51444         e.stopEvent();
51445     },
51446
51447     setContent : function(content, loadScripts){
51448         this.resizeEl.update(content, loadScripts);
51449     }
51450
51451 });
51452
51453
51454
51455
51456
51457
51458
51459
51460
51461 /**
51462  * @class Roo.TreePanel
51463  * @extends Roo.ContentPanel
51464  * @constructor
51465  * Create a new TreePanel. - defaults to fit/scoll contents.
51466  * @param {String/Object} config A string to set only the panel's title, or a config object
51467  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51468  */
51469 Roo.TreePanel = function(config){
51470     var el = config.el;
51471     var tree = config.tree;
51472     delete config.tree; 
51473     delete config.el; // hopefull!
51474     
51475     // wrapper for IE7 strict & safari scroll issue
51476     
51477     var treeEl = el.createChild();
51478     config.resizeEl = treeEl;
51479     
51480     
51481     
51482     Roo.TreePanel.superclass.constructor.call(this, el, config);
51483  
51484  
51485     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51486     //console.log(tree);
51487     this.on('activate', function()
51488     {
51489         if (this.tree.rendered) {
51490             return;
51491         }
51492         //console.log('render tree');
51493         this.tree.render();
51494     });
51495     // this should not be needed.. - it's actually the 'el' that resizes?
51496     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51497     
51498     //this.on('resize',  function (cp, w, h) {
51499     //        this.tree.innerCt.setWidth(w);
51500     //        this.tree.innerCt.setHeight(h);
51501     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51502     //});
51503
51504         
51505     
51506 };
51507
51508 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51509     fitToFrame : true,
51510     autoScroll : true
51511 });
51512
51513
51514
51515
51516
51517
51518
51519
51520
51521
51522
51523 /*
51524  * Based on:
51525  * Ext JS Library 1.1.1
51526  * Copyright(c) 2006-2007, Ext JS, LLC.
51527  *
51528  * Originally Released Under LGPL - original licence link has changed is not relivant.
51529  *
51530  * Fork - LGPL
51531  * <script type="text/javascript">
51532  */
51533  
51534
51535 /**
51536  * @class Roo.ReaderLayout
51537  * @extends Roo.BorderLayout
51538  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51539  * center region containing two nested regions (a top one for a list view and one for item preview below),
51540  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51541  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51542  * expedites the setup of the overall layout and regions for this common application style.
51543  * Example:
51544  <pre><code>
51545 var reader = new Roo.ReaderLayout();
51546 var CP = Roo.ContentPanel;  // shortcut for adding
51547
51548 reader.beginUpdate();
51549 reader.add("north", new CP("north", "North"));
51550 reader.add("west", new CP("west", {title: "West"}));
51551 reader.add("east", new CP("east", {title: "East"}));
51552
51553 reader.regions.listView.add(new CP("listView", "List"));
51554 reader.regions.preview.add(new CP("preview", "Preview"));
51555 reader.endUpdate();
51556 </code></pre>
51557 * @constructor
51558 * Create a new ReaderLayout
51559 * @param {Object} config Configuration options
51560 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51561 * document.body if omitted)
51562 */
51563 Roo.ReaderLayout = function(config, renderTo){
51564     var c = config || {size:{}};
51565     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51566         north: c.north !== false ? Roo.apply({
51567             split:false,
51568             initialSize: 32,
51569             titlebar: false
51570         }, c.north) : false,
51571         west: c.west !== false ? Roo.apply({
51572             split:true,
51573             initialSize: 200,
51574             minSize: 175,
51575             maxSize: 400,
51576             titlebar: true,
51577             collapsible: true,
51578             animate: true,
51579             margins:{left:5,right:0,bottom:5,top:5},
51580             cmargins:{left:5,right:5,bottom:5,top:5}
51581         }, c.west) : false,
51582         east: c.east !== false ? Roo.apply({
51583             split:true,
51584             initialSize: 200,
51585             minSize: 175,
51586             maxSize: 400,
51587             titlebar: true,
51588             collapsible: true,
51589             animate: true,
51590             margins:{left:0,right:5,bottom:5,top:5},
51591             cmargins:{left:5,right:5,bottom:5,top:5}
51592         }, c.east) : false,
51593         center: Roo.apply({
51594             tabPosition: 'top',
51595             autoScroll:false,
51596             closeOnTab: true,
51597             titlebar:false,
51598             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51599         }, c.center)
51600     });
51601
51602     this.el.addClass('x-reader');
51603
51604     this.beginUpdate();
51605
51606     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51607         south: c.preview !== false ? Roo.apply({
51608             split:true,
51609             initialSize: 200,
51610             minSize: 100,
51611             autoScroll:true,
51612             collapsible:true,
51613             titlebar: true,
51614             cmargins:{top:5,left:0, right:0, bottom:0}
51615         }, c.preview) : false,
51616         center: Roo.apply({
51617             autoScroll:false,
51618             titlebar:false,
51619             minHeight:200
51620         }, c.listView)
51621     });
51622     this.add('center', new Roo.NestedLayoutPanel(inner,
51623             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51624
51625     this.endUpdate();
51626
51627     this.regions.preview = inner.getRegion('south');
51628     this.regions.listView = inner.getRegion('center');
51629 };
51630
51631 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51632  * Based on:
51633  * Ext JS Library 1.1.1
51634  * Copyright(c) 2006-2007, Ext JS, LLC.
51635  *
51636  * Originally Released Under LGPL - original licence link has changed is not relivant.
51637  *
51638  * Fork - LGPL
51639  * <script type="text/javascript">
51640  */
51641  
51642 /**
51643  * @class Roo.grid.Grid
51644  * @extends Roo.util.Observable
51645  * This class represents the primary interface of a component based grid control.
51646  * <br><br>Usage:<pre><code>
51647  var grid = new Roo.grid.Grid("my-container-id", {
51648      ds: myDataStore,
51649      cm: myColModel,
51650      selModel: mySelectionModel,
51651      autoSizeColumns: true,
51652      monitorWindowResize: false,
51653      trackMouseOver: true
51654  });
51655  // set any options
51656  grid.render();
51657  * </code></pre>
51658  * <b>Common Problems:</b><br/>
51659  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51660  * element will correct this<br/>
51661  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51662  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51663  * are unpredictable.<br/>
51664  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51665  * grid to calculate dimensions/offsets.<br/>
51666   * @constructor
51667  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51668  * The container MUST have some type of size defined for the grid to fill. The container will be
51669  * automatically set to position relative if it isn't already.
51670  * @param {Object} config A config object that sets properties on this grid.
51671  */
51672 Roo.grid.Grid = function(container, config){
51673         // initialize the container
51674         this.container = Roo.get(container);
51675         this.container.update("");
51676         this.container.setStyle("overflow", "hidden");
51677     this.container.addClass('x-grid-container');
51678
51679     this.id = this.container.id;
51680
51681     Roo.apply(this, config);
51682     // check and correct shorthanded configs
51683     if(this.ds){
51684         this.dataSource = this.ds;
51685         delete this.ds;
51686     }
51687     if(this.cm){
51688         this.colModel = this.cm;
51689         delete this.cm;
51690     }
51691     if(this.sm){
51692         this.selModel = this.sm;
51693         delete this.sm;
51694     }
51695
51696     if (this.selModel) {
51697         this.selModel = Roo.factory(this.selModel, Roo.grid);
51698         this.sm = this.selModel;
51699         this.sm.xmodule = this.xmodule || false;
51700     }
51701     if (typeof(this.colModel.config) == 'undefined') {
51702         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51703         this.cm = this.colModel;
51704         this.cm.xmodule = this.xmodule || false;
51705     }
51706     if (this.dataSource) {
51707         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51708         this.ds = this.dataSource;
51709         this.ds.xmodule = this.xmodule || false;
51710          
51711     }
51712     
51713     
51714     
51715     if(this.width){
51716         this.container.setWidth(this.width);
51717     }
51718
51719     if(this.height){
51720         this.container.setHeight(this.height);
51721     }
51722     /** @private */
51723         this.addEvents({
51724         // raw events
51725         /**
51726          * @event click
51727          * The raw click event for the entire grid.
51728          * @param {Roo.EventObject} e
51729          */
51730         "click" : true,
51731         /**
51732          * @event dblclick
51733          * The raw dblclick event for the entire grid.
51734          * @param {Roo.EventObject} e
51735          */
51736         "dblclick" : true,
51737         /**
51738          * @event contextmenu
51739          * The raw contextmenu event for the entire grid.
51740          * @param {Roo.EventObject} e
51741          */
51742         "contextmenu" : true,
51743         /**
51744          * @event mousedown
51745          * The raw mousedown event for the entire grid.
51746          * @param {Roo.EventObject} e
51747          */
51748         "mousedown" : true,
51749         /**
51750          * @event mouseup
51751          * The raw mouseup event for the entire grid.
51752          * @param {Roo.EventObject} e
51753          */
51754         "mouseup" : true,
51755         /**
51756          * @event mouseover
51757          * The raw mouseover event for the entire grid.
51758          * @param {Roo.EventObject} e
51759          */
51760         "mouseover" : true,
51761         /**
51762          * @event mouseout
51763          * The raw mouseout event for the entire grid.
51764          * @param {Roo.EventObject} e
51765          */
51766         "mouseout" : true,
51767         /**
51768          * @event keypress
51769          * The raw keypress event for the entire grid.
51770          * @param {Roo.EventObject} e
51771          */
51772         "keypress" : true,
51773         /**
51774          * @event keydown
51775          * The raw keydown event for the entire grid.
51776          * @param {Roo.EventObject} e
51777          */
51778         "keydown" : true,
51779
51780         // custom events
51781
51782         /**
51783          * @event cellclick
51784          * Fires when a cell is clicked
51785          * @param {Grid} this
51786          * @param {Number} rowIndex
51787          * @param {Number} columnIndex
51788          * @param {Roo.EventObject} e
51789          */
51790         "cellclick" : true,
51791         /**
51792          * @event celldblclick
51793          * Fires when a cell is double clicked
51794          * @param {Grid} this
51795          * @param {Number} rowIndex
51796          * @param {Number} columnIndex
51797          * @param {Roo.EventObject} e
51798          */
51799         "celldblclick" : true,
51800         /**
51801          * @event rowclick
51802          * Fires when a row is clicked
51803          * @param {Grid} this
51804          * @param {Number} rowIndex
51805          * @param {Roo.EventObject} e
51806          */
51807         "rowclick" : true,
51808         /**
51809          * @event rowdblclick
51810          * Fires when a row is double clicked
51811          * @param {Grid} this
51812          * @param {Number} rowIndex
51813          * @param {Roo.EventObject} e
51814          */
51815         "rowdblclick" : true,
51816         /**
51817          * @event headerclick
51818          * Fires when a header is clicked
51819          * @param {Grid} this
51820          * @param {Number} columnIndex
51821          * @param {Roo.EventObject} e
51822          */
51823         "headerclick" : true,
51824         /**
51825          * @event headerdblclick
51826          * Fires when a header cell is double clicked
51827          * @param {Grid} this
51828          * @param {Number} columnIndex
51829          * @param {Roo.EventObject} e
51830          */
51831         "headerdblclick" : true,
51832         /**
51833          * @event rowcontextmenu
51834          * Fires when a row is right clicked
51835          * @param {Grid} this
51836          * @param {Number} rowIndex
51837          * @param {Roo.EventObject} e
51838          */
51839         "rowcontextmenu" : true,
51840         /**
51841          * @event cellcontextmenu
51842          * Fires when a cell is right clicked
51843          * @param {Grid} this
51844          * @param {Number} rowIndex
51845          * @param {Number} cellIndex
51846          * @param {Roo.EventObject} e
51847          */
51848          "cellcontextmenu" : true,
51849         /**
51850          * @event headercontextmenu
51851          * Fires when a header is right clicked
51852          * @param {Grid} this
51853          * @param {Number} columnIndex
51854          * @param {Roo.EventObject} e
51855          */
51856         "headercontextmenu" : true,
51857         /**
51858          * @event bodyscroll
51859          * Fires when the body element is scrolled
51860          * @param {Number} scrollLeft
51861          * @param {Number} scrollTop
51862          */
51863         "bodyscroll" : true,
51864         /**
51865          * @event columnresize
51866          * Fires when the user resizes a column
51867          * @param {Number} columnIndex
51868          * @param {Number} newSize
51869          */
51870         "columnresize" : true,
51871         /**
51872          * @event columnmove
51873          * Fires when the user moves a column
51874          * @param {Number} oldIndex
51875          * @param {Number} newIndex
51876          */
51877         "columnmove" : true,
51878         /**
51879          * @event startdrag
51880          * Fires when row(s) start being dragged
51881          * @param {Grid} this
51882          * @param {Roo.GridDD} dd The drag drop object
51883          * @param {event} e The raw browser event
51884          */
51885         "startdrag" : true,
51886         /**
51887          * @event enddrag
51888          * Fires when a drag operation is complete
51889          * @param {Grid} this
51890          * @param {Roo.GridDD} dd The drag drop object
51891          * @param {event} e The raw browser event
51892          */
51893         "enddrag" : true,
51894         /**
51895          * @event dragdrop
51896          * Fires when dragged row(s) are dropped on a valid DD target
51897          * @param {Grid} this
51898          * @param {Roo.GridDD} dd The drag drop object
51899          * @param {String} targetId The target drag drop object
51900          * @param {event} e The raw browser event
51901          */
51902         "dragdrop" : true,
51903         /**
51904          * @event dragover
51905          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51906          * @param {Grid} this
51907          * @param {Roo.GridDD} dd The drag drop object
51908          * @param {String} targetId The target drag drop object
51909          * @param {event} e The raw browser event
51910          */
51911         "dragover" : true,
51912         /**
51913          * @event dragenter
51914          *  Fires when the dragged row(s) first cross another DD target while being dragged
51915          * @param {Grid} this
51916          * @param {Roo.GridDD} dd The drag drop object
51917          * @param {String} targetId The target drag drop object
51918          * @param {event} e The raw browser event
51919          */
51920         "dragenter" : true,
51921         /**
51922          * @event dragout
51923          * Fires when the dragged row(s) leave another DD target while being dragged
51924          * @param {Grid} this
51925          * @param {Roo.GridDD} dd The drag drop object
51926          * @param {String} targetId The target drag drop object
51927          * @param {event} e The raw browser event
51928          */
51929         "dragout" : true,
51930         /**
51931          * @event rowclass
51932          * Fires when a row is rendered, so you can change add a style to it.
51933          * @param {GridView} gridview   The grid view
51934          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51935          */
51936         'rowclass' : true,
51937
51938         /**
51939          * @event render
51940          * Fires when the grid is rendered
51941          * @param {Grid} grid
51942          */
51943         'render' : true
51944     });
51945
51946     Roo.grid.Grid.superclass.constructor.call(this);
51947 };
51948 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51949     
51950     /**
51951      * @cfg {String} ddGroup - drag drop group.
51952      */
51953
51954     /**
51955      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51956      */
51957     minColumnWidth : 25,
51958
51959     /**
51960      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51961      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51962      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51963      */
51964     autoSizeColumns : false,
51965
51966     /**
51967      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51968      */
51969     autoSizeHeaders : true,
51970
51971     /**
51972      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51973      */
51974     monitorWindowResize : true,
51975
51976     /**
51977      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51978      * rows measured to get a columns size. Default is 0 (all rows).
51979      */
51980     maxRowsToMeasure : 0,
51981
51982     /**
51983      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51984      */
51985     trackMouseOver : true,
51986
51987     /**
51988     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51989     */
51990     
51991     /**
51992     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51993     */
51994     enableDragDrop : false,
51995     
51996     /**
51997     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51998     */
51999     enableColumnMove : true,
52000     
52001     /**
52002     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52003     */
52004     enableColumnHide : true,
52005     
52006     /**
52007     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52008     */
52009     enableRowHeightSync : false,
52010     
52011     /**
52012     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52013     */
52014     stripeRows : true,
52015     
52016     /**
52017     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52018     */
52019     autoHeight : false,
52020
52021     /**
52022      * @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.
52023      */
52024     autoExpandColumn : false,
52025
52026     /**
52027     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52028     * Default is 50.
52029     */
52030     autoExpandMin : 50,
52031
52032     /**
52033     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52034     */
52035     autoExpandMax : 1000,
52036
52037     /**
52038     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52039     */
52040     view : null,
52041
52042     /**
52043     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52044     */
52045     loadMask : false,
52046     /**
52047     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52048     */
52049     dropTarget: false,
52050     
52051    
52052     
52053     // private
52054     rendered : false,
52055
52056     /**
52057     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52058     * of a fixed width. Default is false.
52059     */
52060     /**
52061     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52062     */
52063     /**
52064      * Called once after all setup has been completed and the grid is ready to be rendered.
52065      * @return {Roo.grid.Grid} this
52066      */
52067     render : function()
52068     {
52069         var c = this.container;
52070         // try to detect autoHeight/width mode
52071         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52072             this.autoHeight = true;
52073         }
52074         var view = this.getView();
52075         view.init(this);
52076
52077         c.on("click", this.onClick, this);
52078         c.on("dblclick", this.onDblClick, this);
52079         c.on("contextmenu", this.onContextMenu, this);
52080         c.on("keydown", this.onKeyDown, this);
52081         if (Roo.isTouch) {
52082             c.on("touchstart", this.onTouchStart, this);
52083         }
52084
52085         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52086
52087         this.getSelectionModel().init(this);
52088
52089         view.render();
52090
52091         if(this.loadMask){
52092             this.loadMask = new Roo.LoadMask(this.container,
52093                     Roo.apply({store:this.dataSource}, this.loadMask));
52094         }
52095         
52096         
52097         if (this.toolbar && this.toolbar.xtype) {
52098             this.toolbar.container = this.getView().getHeaderPanel(true);
52099             this.toolbar = new Roo.Toolbar(this.toolbar);
52100         }
52101         if (this.footer && this.footer.xtype) {
52102             this.footer.dataSource = this.getDataSource();
52103             this.footer.container = this.getView().getFooterPanel(true);
52104             this.footer = Roo.factory(this.footer, Roo);
52105         }
52106         if (this.dropTarget && this.dropTarget.xtype) {
52107             delete this.dropTarget.xtype;
52108             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52109         }
52110         
52111         
52112         this.rendered = true;
52113         this.fireEvent('render', this);
52114         return this;
52115     },
52116
52117         /**
52118          * Reconfigures the grid to use a different Store and Column Model.
52119          * The View will be bound to the new objects and refreshed.
52120          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52121          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52122          */
52123     reconfigure : function(dataSource, colModel){
52124         if(this.loadMask){
52125             this.loadMask.destroy();
52126             this.loadMask = new Roo.LoadMask(this.container,
52127                     Roo.apply({store:dataSource}, this.loadMask));
52128         }
52129         this.view.bind(dataSource, colModel);
52130         this.dataSource = dataSource;
52131         this.colModel = colModel;
52132         this.view.refresh(true);
52133     },
52134
52135     // private
52136     onKeyDown : function(e){
52137         this.fireEvent("keydown", e);
52138     },
52139
52140     /**
52141      * Destroy this grid.
52142      * @param {Boolean} removeEl True to remove the element
52143      */
52144     destroy : function(removeEl, keepListeners){
52145         if(this.loadMask){
52146             this.loadMask.destroy();
52147         }
52148         var c = this.container;
52149         c.removeAllListeners();
52150         this.view.destroy();
52151         this.colModel.purgeListeners();
52152         if(!keepListeners){
52153             this.purgeListeners();
52154         }
52155         c.update("");
52156         if(removeEl === true){
52157             c.remove();
52158         }
52159     },
52160
52161     // private
52162     processEvent : function(name, e){
52163         // does this fire select???
52164         Roo.log('grid:processEvent '  + name);
52165         
52166         if (name != 'touchstart' ) {
52167             this.fireEvent(name, e);    
52168         }
52169         
52170         var t = e.getTarget();
52171         var v = this.view;
52172         var header = v.findHeaderIndex(t);
52173         if(header !== false){
52174             var ename = name == 'touchstart' ? 'click' : name;
52175              
52176             this.fireEvent("header" + ename, this, header, e);
52177         }else{
52178             var row = v.findRowIndex(t);
52179             var cell = v.findCellIndex(t);
52180             if (name == 'touchstart') {
52181                 // first touch is always a click.
52182                 // hopefull this happens after selection is updated.?
52183                 name = false;
52184                 
52185                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52186                     var cs = this.selModel.getSelectedCell();
52187                     if (row == cs[0] && cell == cs[1]){
52188                         name = 'dblclick';
52189                     }
52190                 }
52191                 if (typeof(this.selModel.getSelections) != 'undefined') {
52192                     var cs = this.selModel.getSelections();
52193                     var ds = this.dataSource;
52194                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52195                         name = 'dblclick';
52196                     }
52197                 }
52198                 if (!name) {
52199                     return;
52200                 }
52201             }
52202             
52203             
52204             if(row !== false){
52205                 this.fireEvent("row" + name, this, row, e);
52206                 if(cell !== false){
52207                     this.fireEvent("cell" + name, this, row, cell, e);
52208                 }
52209             }
52210         }
52211     },
52212
52213     // private
52214     onClick : function(e){
52215         this.processEvent("click", e);
52216     },
52217    // private
52218     onTouchStart : function(e){
52219         this.processEvent("touchstart", e);
52220     },
52221
52222     // private
52223     onContextMenu : function(e, t){
52224         this.processEvent("contextmenu", e);
52225     },
52226
52227     // private
52228     onDblClick : function(e){
52229         this.processEvent("dblclick", e);
52230     },
52231
52232     // private
52233     walkCells : function(row, col, step, fn, scope){
52234         var cm = this.colModel, clen = cm.getColumnCount();
52235         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52236         if(step < 0){
52237             if(col < 0){
52238                 row--;
52239                 first = false;
52240             }
52241             while(row >= 0){
52242                 if(!first){
52243                     col = clen-1;
52244                 }
52245                 first = false;
52246                 while(col >= 0){
52247                     if(fn.call(scope || this, row, col, cm) === true){
52248                         return [row, col];
52249                     }
52250                     col--;
52251                 }
52252                 row--;
52253             }
52254         } else {
52255             if(col >= clen){
52256                 row++;
52257                 first = false;
52258             }
52259             while(row < rlen){
52260                 if(!first){
52261                     col = 0;
52262                 }
52263                 first = false;
52264                 while(col < clen){
52265                     if(fn.call(scope || this, row, col, cm) === true){
52266                         return [row, col];
52267                     }
52268                     col++;
52269                 }
52270                 row++;
52271             }
52272         }
52273         return null;
52274     },
52275
52276     // private
52277     getSelections : function(){
52278         return this.selModel.getSelections();
52279     },
52280
52281     /**
52282      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52283      * but if manual update is required this method will initiate it.
52284      */
52285     autoSize : function(){
52286         if(this.rendered){
52287             this.view.layout();
52288             if(this.view.adjustForScroll){
52289                 this.view.adjustForScroll();
52290             }
52291         }
52292     },
52293
52294     /**
52295      * Returns the grid's underlying element.
52296      * @return {Element} The element
52297      */
52298     getGridEl : function(){
52299         return this.container;
52300     },
52301
52302     // private for compatibility, overridden by editor grid
52303     stopEditing : function(){},
52304
52305     /**
52306      * Returns the grid's SelectionModel.
52307      * @return {SelectionModel}
52308      */
52309     getSelectionModel : function(){
52310         if(!this.selModel){
52311             this.selModel = new Roo.grid.RowSelectionModel();
52312         }
52313         return this.selModel;
52314     },
52315
52316     /**
52317      * Returns the grid's DataSource.
52318      * @return {DataSource}
52319      */
52320     getDataSource : function(){
52321         return this.dataSource;
52322     },
52323
52324     /**
52325      * Returns the grid's ColumnModel.
52326      * @return {ColumnModel}
52327      */
52328     getColumnModel : function(){
52329         return this.colModel;
52330     },
52331
52332     /**
52333      * Returns the grid's GridView object.
52334      * @return {GridView}
52335      */
52336     getView : function(){
52337         if(!this.view){
52338             this.view = new Roo.grid.GridView(this.viewConfig);
52339         }
52340         return this.view;
52341     },
52342     /**
52343      * Called to get grid's drag proxy text, by default returns this.ddText.
52344      * @return {String}
52345      */
52346     getDragDropText : function(){
52347         var count = this.selModel.getCount();
52348         return String.format(this.ddText, count, count == 1 ? '' : 's');
52349     }
52350 });
52351 /**
52352  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52353  * %0 is replaced with the number of selected rows.
52354  * @type String
52355  */
52356 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52357  * Based on:
52358  * Ext JS Library 1.1.1
52359  * Copyright(c) 2006-2007, Ext JS, LLC.
52360  *
52361  * Originally Released Under LGPL - original licence link has changed is not relivant.
52362  *
52363  * Fork - LGPL
52364  * <script type="text/javascript">
52365  */
52366  
52367 Roo.grid.AbstractGridView = function(){
52368         this.grid = null;
52369         
52370         this.events = {
52371             "beforerowremoved" : true,
52372             "beforerowsinserted" : true,
52373             "beforerefresh" : true,
52374             "rowremoved" : true,
52375             "rowsinserted" : true,
52376             "rowupdated" : true,
52377             "refresh" : true
52378         };
52379     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52380 };
52381
52382 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52383     rowClass : "x-grid-row",
52384     cellClass : "x-grid-cell",
52385     tdClass : "x-grid-td",
52386     hdClass : "x-grid-hd",
52387     splitClass : "x-grid-hd-split",
52388     
52389     init: function(grid){
52390         this.grid = grid;
52391                 var cid = this.grid.getGridEl().id;
52392         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52393         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52394         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52395         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52396         },
52397         
52398     getColumnRenderers : function(){
52399         var renderers = [];
52400         var cm = this.grid.colModel;
52401         var colCount = cm.getColumnCount();
52402         for(var i = 0; i < colCount; i++){
52403             renderers[i] = cm.getRenderer(i);
52404         }
52405         return renderers;
52406     },
52407     
52408     getColumnIds : function(){
52409         var ids = [];
52410         var cm = this.grid.colModel;
52411         var colCount = cm.getColumnCount();
52412         for(var i = 0; i < colCount; i++){
52413             ids[i] = cm.getColumnId(i);
52414         }
52415         return ids;
52416     },
52417     
52418     getDataIndexes : function(){
52419         if(!this.indexMap){
52420             this.indexMap = this.buildIndexMap();
52421         }
52422         return this.indexMap.colToData;
52423     },
52424     
52425     getColumnIndexByDataIndex : function(dataIndex){
52426         if(!this.indexMap){
52427             this.indexMap = this.buildIndexMap();
52428         }
52429         return this.indexMap.dataToCol[dataIndex];
52430     },
52431     
52432     /**
52433      * Set a css style for a column dynamically. 
52434      * @param {Number} colIndex The index of the column
52435      * @param {String} name The css property name
52436      * @param {String} value The css value
52437      */
52438     setCSSStyle : function(colIndex, name, value){
52439         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52440         Roo.util.CSS.updateRule(selector, name, value);
52441     },
52442     
52443     generateRules : function(cm){
52444         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52445         Roo.util.CSS.removeStyleSheet(rulesId);
52446         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52447             var cid = cm.getColumnId(i);
52448             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52449                          this.tdSelector, cid, " {\n}\n",
52450                          this.hdSelector, cid, " {\n}\n",
52451                          this.splitSelector, cid, " {\n}\n");
52452         }
52453         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52454     }
52455 });/*
52456  * Based on:
52457  * Ext JS Library 1.1.1
52458  * Copyright(c) 2006-2007, Ext JS, LLC.
52459  *
52460  * Originally Released Under LGPL - original licence link has changed is not relivant.
52461  *
52462  * Fork - LGPL
52463  * <script type="text/javascript">
52464  */
52465
52466 // private
52467 // This is a support class used internally by the Grid components
52468 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52469     this.grid = grid;
52470     this.view = grid.getView();
52471     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52472     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52473     if(hd2){
52474         this.setHandleElId(Roo.id(hd));
52475         this.setOuterHandleElId(Roo.id(hd2));
52476     }
52477     this.scroll = false;
52478 };
52479 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52480     maxDragWidth: 120,
52481     getDragData : function(e){
52482         var t = Roo.lib.Event.getTarget(e);
52483         var h = this.view.findHeaderCell(t);
52484         if(h){
52485             return {ddel: h.firstChild, header:h};
52486         }
52487         return false;
52488     },
52489
52490     onInitDrag : function(e){
52491         this.view.headersDisabled = true;
52492         var clone = this.dragData.ddel.cloneNode(true);
52493         clone.id = Roo.id();
52494         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52495         this.proxy.update(clone);
52496         return true;
52497     },
52498
52499     afterValidDrop : function(){
52500         var v = this.view;
52501         setTimeout(function(){
52502             v.headersDisabled = false;
52503         }, 50);
52504     },
52505
52506     afterInvalidDrop : function(){
52507         var v = this.view;
52508         setTimeout(function(){
52509             v.headersDisabled = false;
52510         }, 50);
52511     }
52512 });
52513 /*
52514  * Based on:
52515  * Ext JS Library 1.1.1
52516  * Copyright(c) 2006-2007, Ext JS, LLC.
52517  *
52518  * Originally Released Under LGPL - original licence link has changed is not relivant.
52519  *
52520  * Fork - LGPL
52521  * <script type="text/javascript">
52522  */
52523 // private
52524 // This is a support class used internally by the Grid components
52525 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52526     this.grid = grid;
52527     this.view = grid.getView();
52528     // split the proxies so they don't interfere with mouse events
52529     this.proxyTop = Roo.DomHelper.append(document.body, {
52530         cls:"col-move-top", html:"&#160;"
52531     }, true);
52532     this.proxyBottom = Roo.DomHelper.append(document.body, {
52533         cls:"col-move-bottom", html:"&#160;"
52534     }, true);
52535     this.proxyTop.hide = this.proxyBottom.hide = function(){
52536         this.setLeftTop(-100,-100);
52537         this.setStyle("visibility", "hidden");
52538     };
52539     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52540     // temporarily disabled
52541     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52542     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52543 };
52544 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52545     proxyOffsets : [-4, -9],
52546     fly: Roo.Element.fly,
52547
52548     getTargetFromEvent : function(e){
52549         var t = Roo.lib.Event.getTarget(e);
52550         var cindex = this.view.findCellIndex(t);
52551         if(cindex !== false){
52552             return this.view.getHeaderCell(cindex);
52553         }
52554         return null;
52555     },
52556
52557     nextVisible : function(h){
52558         var v = this.view, cm = this.grid.colModel;
52559         h = h.nextSibling;
52560         while(h){
52561             if(!cm.isHidden(v.getCellIndex(h))){
52562                 return h;
52563             }
52564             h = h.nextSibling;
52565         }
52566         return null;
52567     },
52568
52569     prevVisible : function(h){
52570         var v = this.view, cm = this.grid.colModel;
52571         h = h.prevSibling;
52572         while(h){
52573             if(!cm.isHidden(v.getCellIndex(h))){
52574                 return h;
52575             }
52576             h = h.prevSibling;
52577         }
52578         return null;
52579     },
52580
52581     positionIndicator : function(h, n, e){
52582         var x = Roo.lib.Event.getPageX(e);
52583         var r = Roo.lib.Dom.getRegion(n.firstChild);
52584         var px, pt, py = r.top + this.proxyOffsets[1];
52585         if((r.right - x) <= (r.right-r.left)/2){
52586             px = r.right+this.view.borderWidth;
52587             pt = "after";
52588         }else{
52589             px = r.left;
52590             pt = "before";
52591         }
52592         var oldIndex = this.view.getCellIndex(h);
52593         var newIndex = this.view.getCellIndex(n);
52594
52595         if(this.grid.colModel.isFixed(newIndex)){
52596             return false;
52597         }
52598
52599         var locked = this.grid.colModel.isLocked(newIndex);
52600
52601         if(pt == "after"){
52602             newIndex++;
52603         }
52604         if(oldIndex < newIndex){
52605             newIndex--;
52606         }
52607         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52608             return false;
52609         }
52610         px +=  this.proxyOffsets[0];
52611         this.proxyTop.setLeftTop(px, py);
52612         this.proxyTop.show();
52613         if(!this.bottomOffset){
52614             this.bottomOffset = this.view.mainHd.getHeight();
52615         }
52616         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52617         this.proxyBottom.show();
52618         return pt;
52619     },
52620
52621     onNodeEnter : function(n, dd, e, data){
52622         if(data.header != n){
52623             this.positionIndicator(data.header, n, e);
52624         }
52625     },
52626
52627     onNodeOver : function(n, dd, e, data){
52628         var result = false;
52629         if(data.header != n){
52630             result = this.positionIndicator(data.header, n, e);
52631         }
52632         if(!result){
52633             this.proxyTop.hide();
52634             this.proxyBottom.hide();
52635         }
52636         return result ? this.dropAllowed : this.dropNotAllowed;
52637     },
52638
52639     onNodeOut : function(n, dd, e, data){
52640         this.proxyTop.hide();
52641         this.proxyBottom.hide();
52642     },
52643
52644     onNodeDrop : function(n, dd, e, data){
52645         var h = data.header;
52646         if(h != n){
52647             var cm = this.grid.colModel;
52648             var x = Roo.lib.Event.getPageX(e);
52649             var r = Roo.lib.Dom.getRegion(n.firstChild);
52650             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52651             var oldIndex = this.view.getCellIndex(h);
52652             var newIndex = this.view.getCellIndex(n);
52653             var locked = cm.isLocked(newIndex);
52654             if(pt == "after"){
52655                 newIndex++;
52656             }
52657             if(oldIndex < newIndex){
52658                 newIndex--;
52659             }
52660             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52661                 return false;
52662             }
52663             cm.setLocked(oldIndex, locked, true);
52664             cm.moveColumn(oldIndex, newIndex);
52665             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52666             return true;
52667         }
52668         return false;
52669     }
52670 });
52671 /*
52672  * Based on:
52673  * Ext JS Library 1.1.1
52674  * Copyright(c) 2006-2007, Ext JS, LLC.
52675  *
52676  * Originally Released Under LGPL - original licence link has changed is not relivant.
52677  *
52678  * Fork - LGPL
52679  * <script type="text/javascript">
52680  */
52681   
52682 /**
52683  * @class Roo.grid.GridView
52684  * @extends Roo.util.Observable
52685  *
52686  * @constructor
52687  * @param {Object} config
52688  */
52689 Roo.grid.GridView = function(config){
52690     Roo.grid.GridView.superclass.constructor.call(this);
52691     this.el = null;
52692
52693     Roo.apply(this, config);
52694 };
52695
52696 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52697
52698     unselectable :  'unselectable="on"',
52699     unselectableCls :  'x-unselectable',
52700     
52701     
52702     rowClass : "x-grid-row",
52703
52704     cellClass : "x-grid-col",
52705
52706     tdClass : "x-grid-td",
52707
52708     hdClass : "x-grid-hd",
52709
52710     splitClass : "x-grid-split",
52711
52712     sortClasses : ["sort-asc", "sort-desc"],
52713
52714     enableMoveAnim : false,
52715
52716     hlColor: "C3DAF9",
52717
52718     dh : Roo.DomHelper,
52719
52720     fly : Roo.Element.fly,
52721
52722     css : Roo.util.CSS,
52723
52724     borderWidth: 1,
52725
52726     splitOffset: 3,
52727
52728     scrollIncrement : 22,
52729
52730     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52731
52732     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52733
52734     bind : function(ds, cm){
52735         if(this.ds){
52736             this.ds.un("load", this.onLoad, this);
52737             this.ds.un("datachanged", this.onDataChange, this);
52738             this.ds.un("add", this.onAdd, this);
52739             this.ds.un("remove", this.onRemove, this);
52740             this.ds.un("update", this.onUpdate, this);
52741             this.ds.un("clear", this.onClear, this);
52742         }
52743         if(ds){
52744             ds.on("load", this.onLoad, this);
52745             ds.on("datachanged", this.onDataChange, this);
52746             ds.on("add", this.onAdd, this);
52747             ds.on("remove", this.onRemove, this);
52748             ds.on("update", this.onUpdate, this);
52749             ds.on("clear", this.onClear, this);
52750         }
52751         this.ds = ds;
52752
52753         if(this.cm){
52754             this.cm.un("widthchange", this.onColWidthChange, this);
52755             this.cm.un("headerchange", this.onHeaderChange, this);
52756             this.cm.un("hiddenchange", this.onHiddenChange, this);
52757             this.cm.un("columnmoved", this.onColumnMove, this);
52758             this.cm.un("columnlockchange", this.onColumnLock, this);
52759         }
52760         if(cm){
52761             this.generateRules(cm);
52762             cm.on("widthchange", this.onColWidthChange, this);
52763             cm.on("headerchange", this.onHeaderChange, this);
52764             cm.on("hiddenchange", this.onHiddenChange, this);
52765             cm.on("columnmoved", this.onColumnMove, this);
52766             cm.on("columnlockchange", this.onColumnLock, this);
52767         }
52768         this.cm = cm;
52769     },
52770
52771     init: function(grid){
52772         Roo.grid.GridView.superclass.init.call(this, grid);
52773
52774         this.bind(grid.dataSource, grid.colModel);
52775
52776         grid.on("headerclick", this.handleHeaderClick, this);
52777
52778         if(grid.trackMouseOver){
52779             grid.on("mouseover", this.onRowOver, this);
52780             grid.on("mouseout", this.onRowOut, this);
52781         }
52782         grid.cancelTextSelection = function(){};
52783         this.gridId = grid.id;
52784
52785         var tpls = this.templates || {};
52786
52787         if(!tpls.master){
52788             tpls.master = new Roo.Template(
52789                '<div class="x-grid" hidefocus="true">',
52790                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52791                   '<div class="x-grid-topbar"></div>',
52792                   '<div class="x-grid-scroller"><div></div></div>',
52793                   '<div class="x-grid-locked">',
52794                       '<div class="x-grid-header">{lockedHeader}</div>',
52795                       '<div class="x-grid-body">{lockedBody}</div>',
52796                   "</div>",
52797                   '<div class="x-grid-viewport">',
52798                       '<div class="x-grid-header">{header}</div>',
52799                       '<div class="x-grid-body">{body}</div>',
52800                   "</div>",
52801                   '<div class="x-grid-bottombar"></div>',
52802                  
52803                   '<div class="x-grid-resize-proxy">&#160;</div>',
52804                "</div>"
52805             );
52806             tpls.master.disableformats = true;
52807         }
52808
52809         if(!tpls.header){
52810             tpls.header = new Roo.Template(
52811                '<table border="0" cellspacing="0" cellpadding="0">',
52812                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52813                "</table>{splits}"
52814             );
52815             tpls.header.disableformats = true;
52816         }
52817         tpls.header.compile();
52818
52819         if(!tpls.hcell){
52820             tpls.hcell = new Roo.Template(
52821                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52822                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52823                 "</div></td>"
52824              );
52825              tpls.hcell.disableFormats = true;
52826         }
52827         tpls.hcell.compile();
52828
52829         if(!tpls.hsplit){
52830             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52831                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52832             tpls.hsplit.disableFormats = true;
52833         }
52834         tpls.hsplit.compile();
52835
52836         if(!tpls.body){
52837             tpls.body = new Roo.Template(
52838                '<table border="0" cellspacing="0" cellpadding="0">',
52839                "<tbody>{rows}</tbody>",
52840                "</table>"
52841             );
52842             tpls.body.disableFormats = true;
52843         }
52844         tpls.body.compile();
52845
52846         if(!tpls.row){
52847             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52848             tpls.row.disableFormats = true;
52849         }
52850         tpls.row.compile();
52851
52852         if(!tpls.cell){
52853             tpls.cell = new Roo.Template(
52854                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52855                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52856                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52857                 "</td>"
52858             );
52859             tpls.cell.disableFormats = true;
52860         }
52861         tpls.cell.compile();
52862
52863         this.templates = tpls;
52864     },
52865
52866     // remap these for backwards compat
52867     onColWidthChange : function(){
52868         this.updateColumns.apply(this, arguments);
52869     },
52870     onHeaderChange : function(){
52871         this.updateHeaders.apply(this, arguments);
52872     }, 
52873     onHiddenChange : function(){
52874         this.handleHiddenChange.apply(this, arguments);
52875     },
52876     onColumnMove : function(){
52877         this.handleColumnMove.apply(this, arguments);
52878     },
52879     onColumnLock : function(){
52880         this.handleLockChange.apply(this, arguments);
52881     },
52882
52883     onDataChange : function(){
52884         this.refresh();
52885         this.updateHeaderSortState();
52886     },
52887
52888     onClear : function(){
52889         this.refresh();
52890     },
52891
52892     onUpdate : function(ds, record){
52893         this.refreshRow(record);
52894     },
52895
52896     refreshRow : function(record){
52897         var ds = this.ds, index;
52898         if(typeof record == 'number'){
52899             index = record;
52900             record = ds.getAt(index);
52901         }else{
52902             index = ds.indexOf(record);
52903         }
52904         this.insertRows(ds, index, index, true);
52905         this.onRemove(ds, record, index+1, true);
52906         this.syncRowHeights(index, index);
52907         this.layout();
52908         this.fireEvent("rowupdated", this, index, record);
52909     },
52910
52911     onAdd : function(ds, records, index){
52912         this.insertRows(ds, index, index + (records.length-1));
52913     },
52914
52915     onRemove : function(ds, record, index, isUpdate){
52916         if(isUpdate !== true){
52917             this.fireEvent("beforerowremoved", this, index, record);
52918         }
52919         var bt = this.getBodyTable(), lt = this.getLockedTable();
52920         if(bt.rows[index]){
52921             bt.firstChild.removeChild(bt.rows[index]);
52922         }
52923         if(lt.rows[index]){
52924             lt.firstChild.removeChild(lt.rows[index]);
52925         }
52926         if(isUpdate !== true){
52927             this.stripeRows(index);
52928             this.syncRowHeights(index, index);
52929             this.layout();
52930             this.fireEvent("rowremoved", this, index, record);
52931         }
52932     },
52933
52934     onLoad : function(){
52935         this.scrollToTop();
52936     },
52937
52938     /**
52939      * Scrolls the grid to the top
52940      */
52941     scrollToTop : function(){
52942         if(this.scroller){
52943             this.scroller.dom.scrollTop = 0;
52944             this.syncScroll();
52945         }
52946     },
52947
52948     /**
52949      * Gets a panel in the header of the grid that can be used for toolbars etc.
52950      * After modifying the contents of this panel a call to grid.autoSize() may be
52951      * required to register any changes in size.
52952      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52953      * @return Roo.Element
52954      */
52955     getHeaderPanel : function(doShow){
52956         if(doShow){
52957             this.headerPanel.show();
52958         }
52959         return this.headerPanel;
52960     },
52961
52962     /**
52963      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52964      * After modifying the contents of this panel a call to grid.autoSize() may be
52965      * required to register any changes in size.
52966      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52967      * @return Roo.Element
52968      */
52969     getFooterPanel : function(doShow){
52970         if(doShow){
52971             this.footerPanel.show();
52972         }
52973         return this.footerPanel;
52974     },
52975
52976     initElements : function(){
52977         var E = Roo.Element;
52978         var el = this.grid.getGridEl().dom.firstChild;
52979         var cs = el.childNodes;
52980
52981         this.el = new E(el);
52982         
52983          this.focusEl = new E(el.firstChild);
52984         this.focusEl.swallowEvent("click", true);
52985         
52986         this.headerPanel = new E(cs[1]);
52987         this.headerPanel.enableDisplayMode("block");
52988
52989         this.scroller = new E(cs[2]);
52990         this.scrollSizer = new E(this.scroller.dom.firstChild);
52991
52992         this.lockedWrap = new E(cs[3]);
52993         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52994         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52995
52996         this.mainWrap = new E(cs[4]);
52997         this.mainHd = new E(this.mainWrap.dom.firstChild);
52998         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52999
53000         this.footerPanel = new E(cs[5]);
53001         this.footerPanel.enableDisplayMode("block");
53002
53003         this.resizeProxy = new E(cs[6]);
53004
53005         this.headerSelector = String.format(
53006            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53007            this.lockedHd.id, this.mainHd.id
53008         );
53009
53010         this.splitterSelector = String.format(
53011            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53012            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53013         );
53014     },
53015     idToCssName : function(s)
53016     {
53017         return s.replace(/[^a-z0-9]+/ig, '-');
53018     },
53019
53020     getHeaderCell : function(index){
53021         return Roo.DomQuery.select(this.headerSelector)[index];
53022     },
53023
53024     getHeaderCellMeasure : function(index){
53025         return this.getHeaderCell(index).firstChild;
53026     },
53027
53028     getHeaderCellText : function(index){
53029         return this.getHeaderCell(index).firstChild.firstChild;
53030     },
53031
53032     getLockedTable : function(){
53033         return this.lockedBody.dom.firstChild;
53034     },
53035
53036     getBodyTable : function(){
53037         return this.mainBody.dom.firstChild;
53038     },
53039
53040     getLockedRow : function(index){
53041         return this.getLockedTable().rows[index];
53042     },
53043
53044     getRow : function(index){
53045         return this.getBodyTable().rows[index];
53046     },
53047
53048     getRowComposite : function(index){
53049         if(!this.rowEl){
53050             this.rowEl = new Roo.CompositeElementLite();
53051         }
53052         var els = [], lrow, mrow;
53053         if(lrow = this.getLockedRow(index)){
53054             els.push(lrow);
53055         }
53056         if(mrow = this.getRow(index)){
53057             els.push(mrow);
53058         }
53059         this.rowEl.elements = els;
53060         return this.rowEl;
53061     },
53062     /**
53063      * Gets the 'td' of the cell
53064      * 
53065      * @param {Integer} rowIndex row to select
53066      * @param {Integer} colIndex column to select
53067      * 
53068      * @return {Object} 
53069      */
53070     getCell : function(rowIndex, colIndex){
53071         var locked = this.cm.getLockedCount();
53072         var source;
53073         if(colIndex < locked){
53074             source = this.lockedBody.dom.firstChild;
53075         }else{
53076             source = this.mainBody.dom.firstChild;
53077             colIndex -= locked;
53078         }
53079         return source.rows[rowIndex].childNodes[colIndex];
53080     },
53081
53082     getCellText : function(rowIndex, colIndex){
53083         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53084     },
53085
53086     getCellBox : function(cell){
53087         var b = this.fly(cell).getBox();
53088         if(Roo.isOpera){ // opera fails to report the Y
53089             b.y = cell.offsetTop + this.mainBody.getY();
53090         }
53091         return b;
53092     },
53093
53094     getCellIndex : function(cell){
53095         var id = String(cell.className).match(this.cellRE);
53096         if(id){
53097             return parseInt(id[1], 10);
53098         }
53099         return 0;
53100     },
53101
53102     findHeaderIndex : function(n){
53103         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53104         return r ? this.getCellIndex(r) : false;
53105     },
53106
53107     findHeaderCell : function(n){
53108         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53109         return r ? r : false;
53110     },
53111
53112     findRowIndex : function(n){
53113         if(!n){
53114             return false;
53115         }
53116         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53117         return r ? r.rowIndex : false;
53118     },
53119
53120     findCellIndex : function(node){
53121         var stop = this.el.dom;
53122         while(node && node != stop){
53123             if(this.findRE.test(node.className)){
53124                 return this.getCellIndex(node);
53125             }
53126             node = node.parentNode;
53127         }
53128         return false;
53129     },
53130
53131     getColumnId : function(index){
53132         return this.cm.getColumnId(index);
53133     },
53134
53135     getSplitters : function()
53136     {
53137         if(this.splitterSelector){
53138            return Roo.DomQuery.select(this.splitterSelector);
53139         }else{
53140             return null;
53141       }
53142     },
53143
53144     getSplitter : function(index){
53145         return this.getSplitters()[index];
53146     },
53147
53148     onRowOver : function(e, t){
53149         var row;
53150         if((row = this.findRowIndex(t)) !== false){
53151             this.getRowComposite(row).addClass("x-grid-row-over");
53152         }
53153     },
53154
53155     onRowOut : function(e, t){
53156         var row;
53157         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53158             this.getRowComposite(row).removeClass("x-grid-row-over");
53159         }
53160     },
53161
53162     renderHeaders : function(){
53163         var cm = this.cm;
53164         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53165         var cb = [], lb = [], sb = [], lsb = [], p = {};
53166         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53167             p.cellId = "x-grid-hd-0-" + i;
53168             p.splitId = "x-grid-csplit-0-" + i;
53169             p.id = cm.getColumnId(i);
53170             p.title = cm.getColumnTooltip(i) || "";
53171             p.value = cm.getColumnHeader(i) || "";
53172             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53173             if(!cm.isLocked(i)){
53174                 cb[cb.length] = ct.apply(p);
53175                 sb[sb.length] = st.apply(p);
53176             }else{
53177                 lb[lb.length] = ct.apply(p);
53178                 lsb[lsb.length] = st.apply(p);
53179             }
53180         }
53181         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53182                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53183     },
53184
53185     updateHeaders : function(){
53186         var html = this.renderHeaders();
53187         this.lockedHd.update(html[0]);
53188         this.mainHd.update(html[1]);
53189     },
53190
53191     /**
53192      * Focuses the specified row.
53193      * @param {Number} row The row index
53194      */
53195     focusRow : function(row)
53196     {
53197         //Roo.log('GridView.focusRow');
53198         var x = this.scroller.dom.scrollLeft;
53199         this.focusCell(row, 0, false);
53200         this.scroller.dom.scrollLeft = x;
53201     },
53202
53203     /**
53204      * Focuses the specified cell.
53205      * @param {Number} row The row index
53206      * @param {Number} col The column index
53207      * @param {Boolean} hscroll false to disable horizontal scrolling
53208      */
53209     focusCell : function(row, col, hscroll)
53210     {
53211         //Roo.log('GridView.focusCell');
53212         var el = this.ensureVisible(row, col, hscroll);
53213         this.focusEl.alignTo(el, "tl-tl");
53214         if(Roo.isGecko){
53215             this.focusEl.focus();
53216         }else{
53217             this.focusEl.focus.defer(1, this.focusEl);
53218         }
53219     },
53220
53221     /**
53222      * Scrolls the specified cell into view
53223      * @param {Number} row The row index
53224      * @param {Number} col The column index
53225      * @param {Boolean} hscroll false to disable horizontal scrolling
53226      */
53227     ensureVisible : function(row, col, hscroll)
53228     {
53229         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53230         //return null; //disable for testing.
53231         if(typeof row != "number"){
53232             row = row.rowIndex;
53233         }
53234         if(row < 0 && row >= this.ds.getCount()){
53235             return  null;
53236         }
53237         col = (col !== undefined ? col : 0);
53238         var cm = this.grid.colModel;
53239         while(cm.isHidden(col)){
53240             col++;
53241         }
53242
53243         var el = this.getCell(row, col);
53244         if(!el){
53245             return null;
53246         }
53247         var c = this.scroller.dom;
53248
53249         var ctop = parseInt(el.offsetTop, 10);
53250         var cleft = parseInt(el.offsetLeft, 10);
53251         var cbot = ctop + el.offsetHeight;
53252         var cright = cleft + el.offsetWidth;
53253         
53254         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53255         var stop = parseInt(c.scrollTop, 10);
53256         var sleft = parseInt(c.scrollLeft, 10);
53257         var sbot = stop + ch;
53258         var sright = sleft + c.clientWidth;
53259         /*
53260         Roo.log('GridView.ensureVisible:' +
53261                 ' ctop:' + ctop +
53262                 ' c.clientHeight:' + c.clientHeight +
53263                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53264                 ' stop:' + stop +
53265                 ' cbot:' + cbot +
53266                 ' sbot:' + sbot +
53267                 ' ch:' + ch  
53268                 );
53269         */
53270         if(ctop < stop){
53271              c.scrollTop = ctop;
53272             //Roo.log("set scrolltop to ctop DISABLE?");
53273         }else if(cbot > sbot){
53274             //Roo.log("set scrolltop to cbot-ch");
53275             c.scrollTop = cbot-ch;
53276         }
53277         
53278         if(hscroll !== false){
53279             if(cleft < sleft){
53280                 c.scrollLeft = cleft;
53281             }else if(cright > sright){
53282                 c.scrollLeft = cright-c.clientWidth;
53283             }
53284         }
53285          
53286         return el;
53287     },
53288
53289     updateColumns : function(){
53290         this.grid.stopEditing();
53291         var cm = this.grid.colModel, colIds = this.getColumnIds();
53292         //var totalWidth = cm.getTotalWidth();
53293         var pos = 0;
53294         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53295             //if(cm.isHidden(i)) continue;
53296             var w = cm.getColumnWidth(i);
53297             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53298             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53299         }
53300         this.updateSplitters();
53301     },
53302
53303     generateRules : function(cm){
53304         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53305         Roo.util.CSS.removeStyleSheet(rulesId);
53306         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53307             var cid = cm.getColumnId(i);
53308             var align = '';
53309             if(cm.config[i].align){
53310                 align = 'text-align:'+cm.config[i].align+';';
53311             }
53312             var hidden = '';
53313             if(cm.isHidden(i)){
53314                 hidden = 'display:none;';
53315             }
53316             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53317             ruleBuf.push(
53318                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53319                     this.hdSelector, cid, " {\n", align, width, "}\n",
53320                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53321                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53322         }
53323         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53324     },
53325
53326     updateSplitters : function(){
53327         var cm = this.cm, s = this.getSplitters();
53328         if(s){ // splitters not created yet
53329             var pos = 0, locked = true;
53330             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53331                 if(cm.isHidden(i)) continue;
53332                 var w = cm.getColumnWidth(i); // make sure it's a number
53333                 if(!cm.isLocked(i) && locked){
53334                     pos = 0;
53335                     locked = false;
53336                 }
53337                 pos += w;
53338                 s[i].style.left = (pos-this.splitOffset) + "px";
53339             }
53340         }
53341     },
53342
53343     handleHiddenChange : function(colModel, colIndex, hidden){
53344         if(hidden){
53345             this.hideColumn(colIndex);
53346         }else{
53347             this.unhideColumn(colIndex);
53348         }
53349     },
53350
53351     hideColumn : function(colIndex){
53352         var cid = this.getColumnId(colIndex);
53353         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53354         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53355         if(Roo.isSafari){
53356             this.updateHeaders();
53357         }
53358         this.updateSplitters();
53359         this.layout();
53360     },
53361
53362     unhideColumn : function(colIndex){
53363         var cid = this.getColumnId(colIndex);
53364         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53365         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53366
53367         if(Roo.isSafari){
53368             this.updateHeaders();
53369         }
53370         this.updateSplitters();
53371         this.layout();
53372     },
53373
53374     insertRows : function(dm, firstRow, lastRow, isUpdate){
53375         if(firstRow == 0 && lastRow == dm.getCount()-1){
53376             this.refresh();
53377         }else{
53378             if(!isUpdate){
53379                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53380             }
53381             var s = this.getScrollState();
53382             var markup = this.renderRows(firstRow, lastRow);
53383             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53384             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53385             this.restoreScroll(s);
53386             if(!isUpdate){
53387                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53388                 this.syncRowHeights(firstRow, lastRow);
53389                 this.stripeRows(firstRow);
53390                 this.layout();
53391             }
53392         }
53393     },
53394
53395     bufferRows : function(markup, target, index){
53396         var before = null, trows = target.rows, tbody = target.tBodies[0];
53397         if(index < trows.length){
53398             before = trows[index];
53399         }
53400         var b = document.createElement("div");
53401         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53402         var rows = b.firstChild.rows;
53403         for(var i = 0, len = rows.length; i < len; i++){
53404             if(before){
53405                 tbody.insertBefore(rows[0], before);
53406             }else{
53407                 tbody.appendChild(rows[0]);
53408             }
53409         }
53410         b.innerHTML = "";
53411         b = null;
53412     },
53413
53414     deleteRows : function(dm, firstRow, lastRow){
53415         if(dm.getRowCount()<1){
53416             this.fireEvent("beforerefresh", this);
53417             this.mainBody.update("");
53418             this.lockedBody.update("");
53419             this.fireEvent("refresh", this);
53420         }else{
53421             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53422             var bt = this.getBodyTable();
53423             var tbody = bt.firstChild;
53424             var rows = bt.rows;
53425             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53426                 tbody.removeChild(rows[firstRow]);
53427             }
53428             this.stripeRows(firstRow);
53429             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53430         }
53431     },
53432
53433     updateRows : function(dataSource, firstRow, lastRow){
53434         var s = this.getScrollState();
53435         this.refresh();
53436         this.restoreScroll(s);
53437     },
53438
53439     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53440         if(!noRefresh){
53441            this.refresh();
53442         }
53443         this.updateHeaderSortState();
53444     },
53445
53446     getScrollState : function(){
53447         
53448         var sb = this.scroller.dom;
53449         return {left: sb.scrollLeft, top: sb.scrollTop};
53450     },
53451
53452     stripeRows : function(startRow){
53453         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53454             return;
53455         }
53456         startRow = startRow || 0;
53457         var rows = this.getBodyTable().rows;
53458         var lrows = this.getLockedTable().rows;
53459         var cls = ' x-grid-row-alt ';
53460         for(var i = startRow, len = rows.length; i < len; i++){
53461             var row = rows[i], lrow = lrows[i];
53462             var isAlt = ((i+1) % 2 == 0);
53463             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53464             if(isAlt == hasAlt){
53465                 continue;
53466             }
53467             if(isAlt){
53468                 row.className += " x-grid-row-alt";
53469             }else{
53470                 row.className = row.className.replace("x-grid-row-alt", "");
53471             }
53472             if(lrow){
53473                 lrow.className = row.className;
53474             }
53475         }
53476     },
53477
53478     restoreScroll : function(state){
53479         //Roo.log('GridView.restoreScroll');
53480         var sb = this.scroller.dom;
53481         sb.scrollLeft = state.left;
53482         sb.scrollTop = state.top;
53483         this.syncScroll();
53484     },
53485
53486     syncScroll : function(){
53487         //Roo.log('GridView.syncScroll');
53488         var sb = this.scroller.dom;
53489         var sh = this.mainHd.dom;
53490         var bs = this.mainBody.dom;
53491         var lv = this.lockedBody.dom;
53492         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53493         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53494     },
53495
53496     handleScroll : function(e){
53497         this.syncScroll();
53498         var sb = this.scroller.dom;
53499         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53500         e.stopEvent();
53501     },
53502
53503     handleWheel : function(e){
53504         var d = e.getWheelDelta();
53505         this.scroller.dom.scrollTop -= d*22;
53506         // set this here to prevent jumpy scrolling on large tables
53507         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53508         e.stopEvent();
53509     },
53510
53511     renderRows : function(startRow, endRow){
53512         // pull in all the crap needed to render rows
53513         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53514         var colCount = cm.getColumnCount();
53515
53516         if(ds.getCount() < 1){
53517             return ["", ""];
53518         }
53519
53520         // build a map for all the columns
53521         var cs = [];
53522         for(var i = 0; i < colCount; i++){
53523             var name = cm.getDataIndex(i);
53524             cs[i] = {
53525                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53526                 renderer : cm.getRenderer(i),
53527                 id : cm.getColumnId(i),
53528                 locked : cm.isLocked(i)
53529             };
53530         }
53531
53532         startRow = startRow || 0;
53533         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53534
53535         // records to render
53536         var rs = ds.getRange(startRow, endRow);
53537
53538         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53539     },
53540
53541     // As much as I hate to duplicate code, this was branched because FireFox really hates
53542     // [].join("") on strings. The performance difference was substantial enough to
53543     // branch this function
53544     doRender : Roo.isGecko ?
53545             function(cs, rs, ds, startRow, colCount, stripe){
53546                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53547                 // buffers
53548                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53549                 
53550                 var hasListener = this.grid.hasListener('rowclass');
53551                 var rowcfg = {};
53552                 for(var j = 0, len = rs.length; j < len; j++){
53553                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53554                     for(var i = 0; i < colCount; i++){
53555                         c = cs[i];
53556                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53557                         p.id = c.id;
53558                         p.css = p.attr = "";
53559                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53560                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53561                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53562                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53563                         }
53564                         var markup = ct.apply(p);
53565                         if(!c.locked){
53566                             cb+= markup;
53567                         }else{
53568                             lcb+= markup;
53569                         }
53570                     }
53571                     var alt = [];
53572                     if(stripe && ((rowIndex+1) % 2 == 0)){
53573                         alt.push("x-grid-row-alt")
53574                     }
53575                     if(r.dirty){
53576                         alt.push(  " x-grid-dirty-row");
53577                     }
53578                     rp.cells = lcb;
53579                     if(this.getRowClass){
53580                         alt.push(this.getRowClass(r, rowIndex));
53581                     }
53582                     if (hasListener) {
53583                         rowcfg = {
53584                              
53585                             record: r,
53586                             rowIndex : rowIndex,
53587                             rowClass : ''
53588                         }
53589                         this.grid.fireEvent('rowclass', this, rowcfg);
53590                         alt.push(rowcfg.rowClass);
53591                     }
53592                     rp.alt = alt.join(" ");
53593                     lbuf+= rt.apply(rp);
53594                     rp.cells = cb;
53595                     buf+=  rt.apply(rp);
53596                 }
53597                 return [lbuf, buf];
53598             } :
53599             function(cs, rs, ds, startRow, colCount, stripe){
53600                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53601                 // buffers
53602                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53603                 var hasListener = this.grid.hasListener('rowclass');
53604  
53605                 var rowcfg = {};
53606                 for(var j = 0, len = rs.length; j < len; j++){
53607                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53608                     for(var i = 0; i < colCount; i++){
53609                         c = cs[i];
53610                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53611                         p.id = c.id;
53612                         p.css = p.attr = "";
53613                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53614                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53615                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53616                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53617                         }
53618                         
53619                         var markup = ct.apply(p);
53620                         if(!c.locked){
53621                             cb[cb.length] = markup;
53622                         }else{
53623                             lcb[lcb.length] = markup;
53624                         }
53625                     }
53626                     var alt = [];
53627                     if(stripe && ((rowIndex+1) % 2 == 0)){
53628                         alt.push( "x-grid-row-alt");
53629                     }
53630                     if(r.dirty){
53631                         alt.push(" x-grid-dirty-row");
53632                     }
53633                     rp.cells = lcb;
53634                     if(this.getRowClass){
53635                         alt.push( this.getRowClass(r, rowIndex));
53636                     }
53637                     if (hasListener) {
53638                         rowcfg = {
53639                              
53640                             record: r,
53641                             rowIndex : rowIndex,
53642                             rowClass : ''
53643                         }
53644                         this.grid.fireEvent('rowclass', this, rowcfg);
53645                         alt.push(rowcfg.rowClass);
53646                     }
53647                     rp.alt = alt.join(" ");
53648                     rp.cells = lcb.join("");
53649                     lbuf[lbuf.length] = rt.apply(rp);
53650                     rp.cells = cb.join("");
53651                     buf[buf.length] =  rt.apply(rp);
53652                 }
53653                 return [lbuf.join(""), buf.join("")];
53654             },
53655
53656     renderBody : function(){
53657         var markup = this.renderRows();
53658         var bt = this.templates.body;
53659         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53660     },
53661
53662     /**
53663      * Refreshes the grid
53664      * @param {Boolean} headersToo
53665      */
53666     refresh : function(headersToo){
53667         this.fireEvent("beforerefresh", this);
53668         this.grid.stopEditing();
53669         var result = this.renderBody();
53670         this.lockedBody.update(result[0]);
53671         this.mainBody.update(result[1]);
53672         if(headersToo === true){
53673             this.updateHeaders();
53674             this.updateColumns();
53675             this.updateSplitters();
53676             this.updateHeaderSortState();
53677         }
53678         this.syncRowHeights();
53679         this.layout();
53680         this.fireEvent("refresh", this);
53681     },
53682
53683     handleColumnMove : function(cm, oldIndex, newIndex){
53684         this.indexMap = null;
53685         var s = this.getScrollState();
53686         this.refresh(true);
53687         this.restoreScroll(s);
53688         this.afterMove(newIndex);
53689     },
53690
53691     afterMove : function(colIndex){
53692         if(this.enableMoveAnim && Roo.enableFx){
53693             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53694         }
53695         // if multisort - fix sortOrder, and reload..
53696         if (this.grid.dataSource.multiSort) {
53697             // the we can call sort again..
53698             var dm = this.grid.dataSource;
53699             var cm = this.grid.colModel;
53700             var so = [];
53701             for(var i = 0; i < cm.config.length; i++ ) {
53702                 
53703                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53704                     continue; // dont' bother, it's not in sort list or being set.
53705                 }
53706                 
53707                 so.push(cm.config[i].dataIndex);
53708             };
53709             dm.sortOrder = so;
53710             dm.load(dm.lastOptions);
53711             
53712             
53713         }
53714         
53715     },
53716
53717     updateCell : function(dm, rowIndex, dataIndex){
53718         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53719         if(typeof colIndex == "undefined"){ // not present in grid
53720             return;
53721         }
53722         var cm = this.grid.colModel;
53723         var cell = this.getCell(rowIndex, colIndex);
53724         var cellText = this.getCellText(rowIndex, colIndex);
53725
53726         var p = {
53727             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53728             id : cm.getColumnId(colIndex),
53729             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53730         };
53731         var renderer = cm.getRenderer(colIndex);
53732         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53733         if(typeof val == "undefined" || val === "") val = "&#160;";
53734         cellText.innerHTML = val;
53735         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53736         this.syncRowHeights(rowIndex, rowIndex);
53737     },
53738
53739     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53740         var maxWidth = 0;
53741         if(this.grid.autoSizeHeaders){
53742             var h = this.getHeaderCellMeasure(colIndex);
53743             maxWidth = Math.max(maxWidth, h.scrollWidth);
53744         }
53745         var tb, index;
53746         if(this.cm.isLocked(colIndex)){
53747             tb = this.getLockedTable();
53748             index = colIndex;
53749         }else{
53750             tb = this.getBodyTable();
53751             index = colIndex - this.cm.getLockedCount();
53752         }
53753         if(tb && tb.rows){
53754             var rows = tb.rows;
53755             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53756             for(var i = 0; i < stopIndex; i++){
53757                 var cell = rows[i].childNodes[index].firstChild;
53758                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53759             }
53760         }
53761         return maxWidth + /*margin for error in IE*/ 5;
53762     },
53763     /**
53764      * Autofit a column to its content.
53765      * @param {Number} colIndex
53766      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53767      */
53768      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53769          if(this.cm.isHidden(colIndex)){
53770              return; // can't calc a hidden column
53771          }
53772         if(forceMinSize){
53773             var cid = this.cm.getColumnId(colIndex);
53774             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53775            if(this.grid.autoSizeHeaders){
53776                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53777            }
53778         }
53779         var newWidth = this.calcColumnWidth(colIndex);
53780         this.cm.setColumnWidth(colIndex,
53781             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53782         if(!suppressEvent){
53783             this.grid.fireEvent("columnresize", colIndex, newWidth);
53784         }
53785     },
53786
53787     /**
53788      * Autofits all columns to their content and then expands to fit any extra space in the grid
53789      */
53790      autoSizeColumns : function(){
53791         var cm = this.grid.colModel;
53792         var colCount = cm.getColumnCount();
53793         for(var i = 0; i < colCount; i++){
53794             this.autoSizeColumn(i, true, true);
53795         }
53796         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53797             this.fitColumns();
53798         }else{
53799             this.updateColumns();
53800             this.layout();
53801         }
53802     },
53803
53804     /**
53805      * Autofits all columns to the grid's width proportionate with their current size
53806      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53807      */
53808     fitColumns : function(reserveScrollSpace){
53809         var cm = this.grid.colModel;
53810         var colCount = cm.getColumnCount();
53811         var cols = [];
53812         var width = 0;
53813         var i, w;
53814         for (i = 0; i < colCount; i++){
53815             if(!cm.isHidden(i) && !cm.isFixed(i)){
53816                 w = cm.getColumnWidth(i);
53817                 cols.push(i);
53818                 cols.push(w);
53819                 width += w;
53820             }
53821         }
53822         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53823         if(reserveScrollSpace){
53824             avail -= 17;
53825         }
53826         var frac = (avail - cm.getTotalWidth())/width;
53827         while (cols.length){
53828             w = cols.pop();
53829             i = cols.pop();
53830             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53831         }
53832         this.updateColumns();
53833         this.layout();
53834     },
53835
53836     onRowSelect : function(rowIndex){
53837         var row = this.getRowComposite(rowIndex);
53838         row.addClass("x-grid-row-selected");
53839     },
53840
53841     onRowDeselect : function(rowIndex){
53842         var row = this.getRowComposite(rowIndex);
53843         row.removeClass("x-grid-row-selected");
53844     },
53845
53846     onCellSelect : function(row, col){
53847         var cell = this.getCell(row, col);
53848         if(cell){
53849             Roo.fly(cell).addClass("x-grid-cell-selected");
53850         }
53851     },
53852
53853     onCellDeselect : function(row, col){
53854         var cell = this.getCell(row, col);
53855         if(cell){
53856             Roo.fly(cell).removeClass("x-grid-cell-selected");
53857         }
53858     },
53859
53860     updateHeaderSortState : function(){
53861         
53862         // sort state can be single { field: xxx, direction : yyy}
53863         // or   { xxx=>ASC , yyy : DESC ..... }
53864         
53865         var mstate = {};
53866         if (!this.ds.multiSort) { 
53867             var state = this.ds.getSortState();
53868             if(!state){
53869                 return;
53870             }
53871             mstate[state.field] = state.direction;
53872             // FIXME... - this is not used here.. but might be elsewhere..
53873             this.sortState = state;
53874             
53875         } else {
53876             mstate = this.ds.sortToggle;
53877         }
53878         //remove existing sort classes..
53879         
53880         var sc = this.sortClasses;
53881         var hds = this.el.select(this.headerSelector).removeClass(sc);
53882         
53883         for(var f in mstate) {
53884         
53885             var sortColumn = this.cm.findColumnIndex(f);
53886             
53887             if(sortColumn != -1){
53888                 var sortDir = mstate[f];        
53889                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53890             }
53891         }
53892         
53893          
53894         
53895     },
53896
53897
53898     handleHeaderClick : function(g, index,e){
53899         
53900         Roo.log("header click");
53901         
53902         if (Roo.isTouch) {
53903             // touch events on header are handled by context
53904             this.handleHdCtx(g,index,e);
53905             return;
53906         }
53907         
53908         
53909         if(this.headersDisabled){
53910             return;
53911         }
53912         var dm = g.dataSource, cm = g.colModel;
53913         if(!cm.isSortable(index)){
53914             return;
53915         }
53916         g.stopEditing();
53917         
53918         if (dm.multiSort) {
53919             // update the sortOrder
53920             var so = [];
53921             for(var i = 0; i < cm.config.length; i++ ) {
53922                 
53923                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53924                     continue; // dont' bother, it's not in sort list or being set.
53925                 }
53926                 
53927                 so.push(cm.config[i].dataIndex);
53928             };
53929             dm.sortOrder = so;
53930         }
53931         
53932         
53933         dm.sort(cm.getDataIndex(index));
53934     },
53935
53936
53937     destroy : function(){
53938         if(this.colMenu){
53939             this.colMenu.removeAll();
53940             Roo.menu.MenuMgr.unregister(this.colMenu);
53941             this.colMenu.getEl().remove();
53942             delete this.colMenu;
53943         }
53944         if(this.hmenu){
53945             this.hmenu.removeAll();
53946             Roo.menu.MenuMgr.unregister(this.hmenu);
53947             this.hmenu.getEl().remove();
53948             delete this.hmenu;
53949         }
53950         if(this.grid.enableColumnMove){
53951             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53952             if(dds){
53953                 for(var dd in dds){
53954                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53955                         var elid = dds[dd].dragElId;
53956                         dds[dd].unreg();
53957                         Roo.get(elid).remove();
53958                     } else if(dds[dd].config.isTarget){
53959                         dds[dd].proxyTop.remove();
53960                         dds[dd].proxyBottom.remove();
53961                         dds[dd].unreg();
53962                     }
53963                     if(Roo.dd.DDM.locationCache[dd]){
53964                         delete Roo.dd.DDM.locationCache[dd];
53965                     }
53966                 }
53967                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53968             }
53969         }
53970         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53971         this.bind(null, null);
53972         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53973     },
53974
53975     handleLockChange : function(){
53976         this.refresh(true);
53977     },
53978
53979     onDenyColumnLock : function(){
53980
53981     },
53982
53983     onDenyColumnHide : function(){
53984
53985     },
53986
53987     handleHdMenuClick : function(item){
53988         var index = this.hdCtxIndex;
53989         var cm = this.cm, ds = this.ds;
53990         switch(item.id){
53991             case "asc":
53992                 ds.sort(cm.getDataIndex(index), "ASC");
53993                 break;
53994             case "desc":
53995                 ds.sort(cm.getDataIndex(index), "DESC");
53996                 break;
53997             case "lock":
53998                 var lc = cm.getLockedCount();
53999                 if(cm.getColumnCount(true) <= lc+1){
54000                     this.onDenyColumnLock();
54001                     return;
54002                 }
54003                 if(lc != index){
54004                     cm.setLocked(index, true, true);
54005                     cm.moveColumn(index, lc);
54006                     this.grid.fireEvent("columnmove", index, lc);
54007                 }else{
54008                     cm.setLocked(index, true);
54009                 }
54010             break;
54011             case "unlock":
54012                 var lc = cm.getLockedCount();
54013                 if((lc-1) != index){
54014                     cm.setLocked(index, false, true);
54015                     cm.moveColumn(index, lc-1);
54016                     this.grid.fireEvent("columnmove", index, lc-1);
54017                 }else{
54018                     cm.setLocked(index, false);
54019                 }
54020             break;
54021             case 'wider': // used to expand cols on touch..
54022             case 'narrow':
54023                 var cw = cm.getColumnWidth(index);
54024                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54025                 cw = Math.max(0, cw);
54026                 cw = Math.min(cw,4000);
54027                 cm.setColumnWidth(index, cw);
54028                 break;
54029                 
54030             default:
54031                 index = cm.getIndexById(item.id.substr(4));
54032                 if(index != -1){
54033                     if(item.checked && cm.getColumnCount(true) <= 1){
54034                         this.onDenyColumnHide();
54035                         return false;
54036                     }
54037                     cm.setHidden(index, item.checked);
54038                 }
54039         }
54040         return true;
54041     },
54042
54043     beforeColMenuShow : function(){
54044         var cm = this.cm,  colCount = cm.getColumnCount();
54045         this.colMenu.removeAll();
54046         for(var i = 0; i < colCount; i++){
54047             this.colMenu.add(new Roo.menu.CheckItem({
54048                 id: "col-"+cm.getColumnId(i),
54049                 text: cm.getColumnHeader(i),
54050                 checked: !cm.isHidden(i),
54051                 hideOnClick:false
54052             }));
54053         }
54054     },
54055
54056     handleHdCtx : function(g, index, e){
54057         e.stopEvent();
54058         var hd = this.getHeaderCell(index);
54059         this.hdCtxIndex = index;
54060         var ms = this.hmenu.items, cm = this.cm;
54061         ms.get("asc").setDisabled(!cm.isSortable(index));
54062         ms.get("desc").setDisabled(!cm.isSortable(index));
54063         if(this.grid.enableColLock !== false){
54064             ms.get("lock").setDisabled(cm.isLocked(index));
54065             ms.get("unlock").setDisabled(!cm.isLocked(index));
54066         }
54067         this.hmenu.show(hd, "tl-bl");
54068     },
54069
54070     handleHdOver : function(e){
54071         var hd = this.findHeaderCell(e.getTarget());
54072         if(hd && !this.headersDisabled){
54073             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54074                this.fly(hd).addClass("x-grid-hd-over");
54075             }
54076         }
54077     },
54078
54079     handleHdOut : function(e){
54080         var hd = this.findHeaderCell(e.getTarget());
54081         if(hd){
54082             this.fly(hd).removeClass("x-grid-hd-over");
54083         }
54084     },
54085
54086     handleSplitDblClick : function(e, t){
54087         var i = this.getCellIndex(t);
54088         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54089             this.autoSizeColumn(i, true);
54090             this.layout();
54091         }
54092     },
54093
54094     render : function(){
54095
54096         var cm = this.cm;
54097         var colCount = cm.getColumnCount();
54098
54099         if(this.grid.monitorWindowResize === true){
54100             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54101         }
54102         var header = this.renderHeaders();
54103         var body = this.templates.body.apply({rows:""});
54104         var html = this.templates.master.apply({
54105             lockedBody: body,
54106             body: body,
54107             lockedHeader: header[0],
54108             header: header[1]
54109         });
54110
54111         //this.updateColumns();
54112
54113         this.grid.getGridEl().dom.innerHTML = html;
54114
54115         this.initElements();
54116         
54117         // a kludge to fix the random scolling effect in webkit
54118         this.el.on("scroll", function() {
54119             this.el.dom.scrollTop=0; // hopefully not recursive..
54120         },this);
54121
54122         this.scroller.on("scroll", this.handleScroll, this);
54123         this.lockedBody.on("mousewheel", this.handleWheel, this);
54124         this.mainBody.on("mousewheel", this.handleWheel, this);
54125
54126         this.mainHd.on("mouseover", this.handleHdOver, this);
54127         this.mainHd.on("mouseout", this.handleHdOut, this);
54128         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54129                 {delegate: "."+this.splitClass});
54130
54131         this.lockedHd.on("mouseover", this.handleHdOver, this);
54132         this.lockedHd.on("mouseout", this.handleHdOut, this);
54133         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54134                 {delegate: "."+this.splitClass});
54135
54136         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54137             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54138         }
54139
54140         this.updateSplitters();
54141
54142         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54143             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54144             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54145         }
54146
54147         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54148             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54149             this.hmenu.add(
54150                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54151                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54152             );
54153             if(this.grid.enableColLock !== false){
54154                 this.hmenu.add('-',
54155                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54156                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54157                 );
54158             }
54159             if (Roo.isTouch) {
54160                  this.hmenu.add('-',
54161                     {id:"wider", text: this.columnsWiderText},
54162                     {id:"narrow", text: this.columnsNarrowText }
54163                 );
54164                 
54165                  
54166             }
54167             
54168             if(this.grid.enableColumnHide !== false){
54169
54170                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54171                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54172                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54173
54174                 this.hmenu.add('-',
54175                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54176                 );
54177             }
54178             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54179
54180             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54181         }
54182
54183         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54184             this.dd = new Roo.grid.GridDragZone(this.grid, {
54185                 ddGroup : this.grid.ddGroup || 'GridDD'
54186             });
54187             
54188         }
54189
54190         /*
54191         for(var i = 0; i < colCount; i++){
54192             if(cm.isHidden(i)){
54193                 this.hideColumn(i);
54194             }
54195             if(cm.config[i].align){
54196                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54197                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54198             }
54199         }*/
54200         
54201         this.updateHeaderSortState();
54202
54203         this.beforeInitialResize();
54204         this.layout(true);
54205
54206         // two part rendering gives faster view to the user
54207         this.renderPhase2.defer(1, this);
54208     },
54209
54210     renderPhase2 : function(){
54211         // render the rows now
54212         this.refresh();
54213         if(this.grid.autoSizeColumns){
54214             this.autoSizeColumns();
54215         }
54216     },
54217
54218     beforeInitialResize : function(){
54219
54220     },
54221
54222     onColumnSplitterMoved : function(i, w){
54223         this.userResized = true;
54224         var cm = this.grid.colModel;
54225         cm.setColumnWidth(i, w, true);
54226         var cid = cm.getColumnId(i);
54227         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54228         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54229         this.updateSplitters();
54230         this.layout();
54231         this.grid.fireEvent("columnresize", i, w);
54232     },
54233
54234     syncRowHeights : function(startIndex, endIndex){
54235         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54236             startIndex = startIndex || 0;
54237             var mrows = this.getBodyTable().rows;
54238             var lrows = this.getLockedTable().rows;
54239             var len = mrows.length-1;
54240             endIndex = Math.min(endIndex || len, len);
54241             for(var i = startIndex; i <= endIndex; i++){
54242                 var m = mrows[i], l = lrows[i];
54243                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54244                 m.style.height = l.style.height = h + "px";
54245             }
54246         }
54247     },
54248
54249     layout : function(initialRender, is2ndPass){
54250         var g = this.grid;
54251         var auto = g.autoHeight;
54252         var scrollOffset = 16;
54253         var c = g.getGridEl(), cm = this.cm,
54254                 expandCol = g.autoExpandColumn,
54255                 gv = this;
54256         //c.beginMeasure();
54257
54258         if(!c.dom.offsetWidth){ // display:none?
54259             if(initialRender){
54260                 this.lockedWrap.show();
54261                 this.mainWrap.show();
54262             }
54263             return;
54264         }
54265
54266         var hasLock = this.cm.isLocked(0);
54267
54268         var tbh = this.headerPanel.getHeight();
54269         var bbh = this.footerPanel.getHeight();
54270
54271         if(auto){
54272             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54273             var newHeight = ch + c.getBorderWidth("tb");
54274             if(g.maxHeight){
54275                 newHeight = Math.min(g.maxHeight, newHeight);
54276             }
54277             c.setHeight(newHeight);
54278         }
54279
54280         if(g.autoWidth){
54281             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54282         }
54283
54284         var s = this.scroller;
54285
54286         var csize = c.getSize(true);
54287
54288         this.el.setSize(csize.width, csize.height);
54289
54290         this.headerPanel.setWidth(csize.width);
54291         this.footerPanel.setWidth(csize.width);
54292
54293         var hdHeight = this.mainHd.getHeight();
54294         var vw = csize.width;
54295         var vh = csize.height - (tbh + bbh);
54296
54297         s.setSize(vw, vh);
54298
54299         var bt = this.getBodyTable();
54300         var ltWidth = hasLock ?
54301                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54302
54303         var scrollHeight = bt.offsetHeight;
54304         var scrollWidth = ltWidth + bt.offsetWidth;
54305         var vscroll = false, hscroll = false;
54306
54307         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54308
54309         var lw = this.lockedWrap, mw = this.mainWrap;
54310         var lb = this.lockedBody, mb = this.mainBody;
54311
54312         setTimeout(function(){
54313             var t = s.dom.offsetTop;
54314             var w = s.dom.clientWidth,
54315                 h = s.dom.clientHeight;
54316
54317             lw.setTop(t);
54318             lw.setSize(ltWidth, h);
54319
54320             mw.setLeftTop(ltWidth, t);
54321             mw.setSize(w-ltWidth, h);
54322
54323             lb.setHeight(h-hdHeight);
54324             mb.setHeight(h-hdHeight);
54325
54326             if(is2ndPass !== true && !gv.userResized && expandCol){
54327                 // high speed resize without full column calculation
54328                 
54329                 var ci = cm.getIndexById(expandCol);
54330                 if (ci < 0) {
54331                     ci = cm.findColumnIndex(expandCol);
54332                 }
54333                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54334                 var expandId = cm.getColumnId(ci);
54335                 var  tw = cm.getTotalWidth(false);
54336                 var currentWidth = cm.getColumnWidth(ci);
54337                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54338                 if(currentWidth != cw){
54339                     cm.setColumnWidth(ci, cw, true);
54340                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54341                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54342                     gv.updateSplitters();
54343                     gv.layout(false, true);
54344                 }
54345             }
54346
54347             if(initialRender){
54348                 lw.show();
54349                 mw.show();
54350             }
54351             //c.endMeasure();
54352         }, 10);
54353     },
54354
54355     onWindowResize : function(){
54356         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54357             return;
54358         }
54359         this.layout();
54360     },
54361
54362     appendFooter : function(parentEl){
54363         return null;
54364     },
54365
54366     sortAscText : "Sort Ascending",
54367     sortDescText : "Sort Descending",
54368     lockText : "Lock Column",
54369     unlockText : "Unlock Column",
54370     columnsText : "Columns",
54371  
54372     columnsWiderText : "Wider",
54373     columnsNarrowText : "Thinner"
54374 });
54375
54376
54377 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54378     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54379     this.proxy.el.addClass('x-grid3-col-dd');
54380 };
54381
54382 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54383     handleMouseDown : function(e){
54384
54385     },
54386
54387     callHandleMouseDown : function(e){
54388         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54389     }
54390 });
54391 /*
54392  * Based on:
54393  * Ext JS Library 1.1.1
54394  * Copyright(c) 2006-2007, Ext JS, LLC.
54395  *
54396  * Originally Released Under LGPL - original licence link has changed is not relivant.
54397  *
54398  * Fork - LGPL
54399  * <script type="text/javascript">
54400  */
54401  
54402 // private
54403 // This is a support class used internally by the Grid components
54404 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54405     this.grid = grid;
54406     this.view = grid.getView();
54407     this.proxy = this.view.resizeProxy;
54408     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54409         "gridSplitters" + this.grid.getGridEl().id, {
54410         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54411     });
54412     this.setHandleElId(Roo.id(hd));
54413     this.setOuterHandleElId(Roo.id(hd2));
54414     this.scroll = false;
54415 };
54416 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54417     fly: Roo.Element.fly,
54418
54419     b4StartDrag : function(x, y){
54420         this.view.headersDisabled = true;
54421         this.proxy.setHeight(this.view.mainWrap.getHeight());
54422         var w = this.cm.getColumnWidth(this.cellIndex);
54423         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54424         this.resetConstraints();
54425         this.setXConstraint(minw, 1000);
54426         this.setYConstraint(0, 0);
54427         this.minX = x - minw;
54428         this.maxX = x + 1000;
54429         this.startPos = x;
54430         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54431     },
54432
54433
54434     handleMouseDown : function(e){
54435         ev = Roo.EventObject.setEvent(e);
54436         var t = this.fly(ev.getTarget());
54437         if(t.hasClass("x-grid-split")){
54438             this.cellIndex = this.view.getCellIndex(t.dom);
54439             this.split = t.dom;
54440             this.cm = this.grid.colModel;
54441             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54442                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54443             }
54444         }
54445     },
54446
54447     endDrag : function(e){
54448         this.view.headersDisabled = false;
54449         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54450         var diff = endX - this.startPos;
54451         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54452     },
54453
54454     autoOffset : function(){
54455         this.setDelta(0,0);
54456     }
54457 });/*
54458  * Based on:
54459  * Ext JS Library 1.1.1
54460  * Copyright(c) 2006-2007, Ext JS, LLC.
54461  *
54462  * Originally Released Under LGPL - original licence link has changed is not relivant.
54463  *
54464  * Fork - LGPL
54465  * <script type="text/javascript">
54466  */
54467  
54468 // private
54469 // This is a support class used internally by the Grid components
54470 Roo.grid.GridDragZone = function(grid, config){
54471     this.view = grid.getView();
54472     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54473     if(this.view.lockedBody){
54474         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54475         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54476     }
54477     this.scroll = false;
54478     this.grid = grid;
54479     this.ddel = document.createElement('div');
54480     this.ddel.className = 'x-grid-dd-wrap';
54481 };
54482
54483 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54484     ddGroup : "GridDD",
54485
54486     getDragData : function(e){
54487         var t = Roo.lib.Event.getTarget(e);
54488         var rowIndex = this.view.findRowIndex(t);
54489         var sm = this.grid.selModel;
54490             
54491         //Roo.log(rowIndex);
54492         
54493         if (sm.getSelectedCell) {
54494             // cell selection..
54495             if (!sm.getSelectedCell()) {
54496                 return false;
54497             }
54498             if (rowIndex != sm.getSelectedCell()[0]) {
54499                 return false;
54500             }
54501         
54502         }
54503         
54504         if(rowIndex !== false){
54505             
54506             // if editorgrid.. 
54507             
54508             
54509             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54510                
54511             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54512               //  
54513             //}
54514             if (e.hasModifier()){
54515                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54516             }
54517             
54518             Roo.log("getDragData");
54519             
54520             return {
54521                 grid: this.grid,
54522                 ddel: this.ddel,
54523                 rowIndex: rowIndex,
54524                 selections:sm.getSelections ? sm.getSelections() : (
54525                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54526                 )
54527             };
54528         }
54529         return false;
54530     },
54531
54532     onInitDrag : function(e){
54533         var data = this.dragData;
54534         this.ddel.innerHTML = this.grid.getDragDropText();
54535         this.proxy.update(this.ddel);
54536         // fire start drag?
54537     },
54538
54539     afterRepair : function(){
54540         this.dragging = false;
54541     },
54542
54543     getRepairXY : function(e, data){
54544         return false;
54545     },
54546
54547     onEndDrag : function(data, e){
54548         // fire end drag?
54549     },
54550
54551     onValidDrop : function(dd, e, id){
54552         // fire drag drop?
54553         this.hideProxy();
54554     },
54555
54556     beforeInvalidDrop : function(e, id){
54557
54558     }
54559 });/*
54560  * Based on:
54561  * Ext JS Library 1.1.1
54562  * Copyright(c) 2006-2007, Ext JS, LLC.
54563  *
54564  * Originally Released Under LGPL - original licence link has changed is not relivant.
54565  *
54566  * Fork - LGPL
54567  * <script type="text/javascript">
54568  */
54569  
54570
54571 /**
54572  * @class Roo.grid.ColumnModel
54573  * @extends Roo.util.Observable
54574  * This is the default implementation of a ColumnModel used by the Grid. It defines
54575  * the columns in the grid.
54576  * <br>Usage:<br>
54577  <pre><code>
54578  var colModel = new Roo.grid.ColumnModel([
54579         {header: "Ticker", width: 60, sortable: true, locked: true},
54580         {header: "Company Name", width: 150, sortable: true},
54581         {header: "Market Cap.", width: 100, sortable: true},
54582         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54583         {header: "Employees", width: 100, sortable: true, resizable: false}
54584  ]);
54585  </code></pre>
54586  * <p>
54587  
54588  * The config options listed for this class are options which may appear in each
54589  * individual column definition.
54590  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54591  * @constructor
54592  * @param {Object} config An Array of column config objects. See this class's
54593  * config objects for details.
54594 */
54595 Roo.grid.ColumnModel = function(config){
54596         /**
54597      * The config passed into the constructor
54598      */
54599     this.config = config;
54600     this.lookup = {};
54601
54602     // if no id, create one
54603     // if the column does not have a dataIndex mapping,
54604     // map it to the order it is in the config
54605     for(var i = 0, len = config.length; i < len; i++){
54606         var c = config[i];
54607         if(typeof c.dataIndex == "undefined"){
54608             c.dataIndex = i;
54609         }
54610         if(typeof c.renderer == "string"){
54611             c.renderer = Roo.util.Format[c.renderer];
54612         }
54613         if(typeof c.id == "undefined"){
54614             c.id = Roo.id();
54615         }
54616         if(c.editor && c.editor.xtype){
54617             c.editor  = Roo.factory(c.editor, Roo.grid);
54618         }
54619         if(c.editor && c.editor.isFormField){
54620             c.editor = new Roo.grid.GridEditor(c.editor);
54621         }
54622         this.lookup[c.id] = c;
54623     }
54624
54625     /**
54626      * The width of columns which have no width specified (defaults to 100)
54627      * @type Number
54628      */
54629     this.defaultWidth = 100;
54630
54631     /**
54632      * Default sortable of columns which have no sortable specified (defaults to false)
54633      * @type Boolean
54634      */
54635     this.defaultSortable = false;
54636
54637     this.addEvents({
54638         /**
54639              * @event widthchange
54640              * Fires when the width of a column changes.
54641              * @param {ColumnModel} this
54642              * @param {Number} columnIndex The column index
54643              * @param {Number} newWidth The new width
54644              */
54645             "widthchange": true,
54646         /**
54647              * @event headerchange
54648              * Fires when the text of a header changes.
54649              * @param {ColumnModel} this
54650              * @param {Number} columnIndex The column index
54651              * @param {Number} newText The new header text
54652              */
54653             "headerchange": true,
54654         /**
54655              * @event hiddenchange
54656              * Fires when a column is hidden or "unhidden".
54657              * @param {ColumnModel} this
54658              * @param {Number} columnIndex The column index
54659              * @param {Boolean} hidden true if hidden, false otherwise
54660              */
54661             "hiddenchange": true,
54662             /**
54663          * @event columnmoved
54664          * Fires when a column is moved.
54665          * @param {ColumnModel} this
54666          * @param {Number} oldIndex
54667          * @param {Number} newIndex
54668          */
54669         "columnmoved" : true,
54670         /**
54671          * @event columlockchange
54672          * Fires when a column's locked state is changed
54673          * @param {ColumnModel} this
54674          * @param {Number} colIndex
54675          * @param {Boolean} locked true if locked
54676          */
54677         "columnlockchange" : true
54678     });
54679     Roo.grid.ColumnModel.superclass.constructor.call(this);
54680 };
54681 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54682     /**
54683      * @cfg {String} header The header text to display in the Grid view.
54684      */
54685     /**
54686      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54687      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54688      * specified, the column's index is used as an index into the Record's data Array.
54689      */
54690     /**
54691      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54692      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54693      */
54694     /**
54695      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54696      * Defaults to the value of the {@link #defaultSortable} property.
54697      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54698      */
54699     /**
54700      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54701      */
54702     /**
54703      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54704      */
54705     /**
54706      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54707      */
54708     /**
54709      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54710      */
54711     /**
54712      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54713      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54714      * default renderer uses the raw data value.
54715      */
54716        /**
54717      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54718      */
54719     /**
54720      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54721      */
54722
54723     /**
54724      * Returns the id of the column at the specified index.
54725      * @param {Number} index The column index
54726      * @return {String} the id
54727      */
54728     getColumnId : function(index){
54729         return this.config[index].id;
54730     },
54731
54732     /**
54733      * Returns the column for a specified id.
54734      * @param {String} id The column id
54735      * @return {Object} the column
54736      */
54737     getColumnById : function(id){
54738         return this.lookup[id];
54739     },
54740
54741     
54742     /**
54743      * Returns the column for a specified dataIndex.
54744      * @param {String} dataIndex The column dataIndex
54745      * @return {Object|Boolean} the column or false if not found
54746      */
54747     getColumnByDataIndex: function(dataIndex){
54748         var index = this.findColumnIndex(dataIndex);
54749         return index > -1 ? this.config[index] : false;
54750     },
54751     
54752     /**
54753      * Returns the index for a specified column id.
54754      * @param {String} id The column id
54755      * @return {Number} the index, or -1 if not found
54756      */
54757     getIndexById : function(id){
54758         for(var i = 0, len = this.config.length; i < len; i++){
54759             if(this.config[i].id == id){
54760                 return i;
54761             }
54762         }
54763         return -1;
54764     },
54765     
54766     /**
54767      * Returns the index for a specified column dataIndex.
54768      * @param {String} dataIndex The column dataIndex
54769      * @return {Number} the index, or -1 if not found
54770      */
54771     
54772     findColumnIndex : function(dataIndex){
54773         for(var i = 0, len = this.config.length; i < len; i++){
54774             if(this.config[i].dataIndex == dataIndex){
54775                 return i;
54776             }
54777         }
54778         return -1;
54779     },
54780     
54781     
54782     moveColumn : function(oldIndex, newIndex){
54783         var c = this.config[oldIndex];
54784         this.config.splice(oldIndex, 1);
54785         this.config.splice(newIndex, 0, c);
54786         this.dataMap = null;
54787         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54788     },
54789
54790     isLocked : function(colIndex){
54791         return this.config[colIndex].locked === true;
54792     },
54793
54794     setLocked : function(colIndex, value, suppressEvent){
54795         if(this.isLocked(colIndex) == value){
54796             return;
54797         }
54798         this.config[colIndex].locked = value;
54799         if(!suppressEvent){
54800             this.fireEvent("columnlockchange", this, colIndex, value);
54801         }
54802     },
54803
54804     getTotalLockedWidth : function(){
54805         var totalWidth = 0;
54806         for(var i = 0; i < this.config.length; i++){
54807             if(this.isLocked(i) && !this.isHidden(i)){
54808                 this.totalWidth += this.getColumnWidth(i);
54809             }
54810         }
54811         return totalWidth;
54812     },
54813
54814     getLockedCount : function(){
54815         for(var i = 0, len = this.config.length; i < len; i++){
54816             if(!this.isLocked(i)){
54817                 return i;
54818             }
54819         }
54820     },
54821
54822     /**
54823      * Returns the number of columns.
54824      * @return {Number}
54825      */
54826     getColumnCount : function(visibleOnly){
54827         if(visibleOnly === true){
54828             var c = 0;
54829             for(var i = 0, len = this.config.length; i < len; i++){
54830                 if(!this.isHidden(i)){
54831                     c++;
54832                 }
54833             }
54834             return c;
54835         }
54836         return this.config.length;
54837     },
54838
54839     /**
54840      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54841      * @param {Function} fn
54842      * @param {Object} scope (optional)
54843      * @return {Array} result
54844      */
54845     getColumnsBy : function(fn, scope){
54846         var r = [];
54847         for(var i = 0, len = this.config.length; i < len; i++){
54848             var c = this.config[i];
54849             if(fn.call(scope||this, c, i) === true){
54850                 r[r.length] = c;
54851             }
54852         }
54853         return r;
54854     },
54855
54856     /**
54857      * Returns true if the specified column is sortable.
54858      * @param {Number} col The column index
54859      * @return {Boolean}
54860      */
54861     isSortable : function(col){
54862         if(typeof this.config[col].sortable == "undefined"){
54863             return this.defaultSortable;
54864         }
54865         return this.config[col].sortable;
54866     },
54867
54868     /**
54869      * Returns the rendering (formatting) function defined for the column.
54870      * @param {Number} col The column index.
54871      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54872      */
54873     getRenderer : function(col){
54874         if(!this.config[col].renderer){
54875             return Roo.grid.ColumnModel.defaultRenderer;
54876         }
54877         return this.config[col].renderer;
54878     },
54879
54880     /**
54881      * Sets the rendering (formatting) function for a column.
54882      * @param {Number} col The column index
54883      * @param {Function} fn The function to use to process the cell's raw data
54884      * to return HTML markup for the grid view. The render function is called with
54885      * the following parameters:<ul>
54886      * <li>Data value.</li>
54887      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54888      * <li>css A CSS style string to apply to the table cell.</li>
54889      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54890      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54891      * <li>Row index</li>
54892      * <li>Column index</li>
54893      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54894      */
54895     setRenderer : function(col, fn){
54896         this.config[col].renderer = fn;
54897     },
54898
54899     /**
54900      * Returns the width for the specified column.
54901      * @param {Number} col The column index
54902      * @return {Number}
54903      */
54904     getColumnWidth : function(col){
54905         return this.config[col].width * 1 || this.defaultWidth;
54906     },
54907
54908     /**
54909      * Sets the width for a column.
54910      * @param {Number} col The column index
54911      * @param {Number} width The new width
54912      */
54913     setColumnWidth : function(col, width, suppressEvent){
54914         this.config[col].width = width;
54915         this.totalWidth = null;
54916         if(!suppressEvent){
54917              this.fireEvent("widthchange", this, col, width);
54918         }
54919     },
54920
54921     /**
54922      * Returns the total width of all columns.
54923      * @param {Boolean} includeHidden True to include hidden column widths
54924      * @return {Number}
54925      */
54926     getTotalWidth : function(includeHidden){
54927         if(!this.totalWidth){
54928             this.totalWidth = 0;
54929             for(var i = 0, len = this.config.length; i < len; i++){
54930                 if(includeHidden || !this.isHidden(i)){
54931                     this.totalWidth += this.getColumnWidth(i);
54932                 }
54933             }
54934         }
54935         return this.totalWidth;
54936     },
54937
54938     /**
54939      * Returns the header for the specified column.
54940      * @param {Number} col The column index
54941      * @return {String}
54942      */
54943     getColumnHeader : function(col){
54944         return this.config[col].header;
54945     },
54946
54947     /**
54948      * Sets the header for a column.
54949      * @param {Number} col The column index
54950      * @param {String} header The new header
54951      */
54952     setColumnHeader : function(col, header){
54953         this.config[col].header = header;
54954         this.fireEvent("headerchange", this, col, header);
54955     },
54956
54957     /**
54958      * Returns the tooltip for the specified column.
54959      * @param {Number} col The column index
54960      * @return {String}
54961      */
54962     getColumnTooltip : function(col){
54963             return this.config[col].tooltip;
54964     },
54965     /**
54966      * Sets the tooltip for a column.
54967      * @param {Number} col The column index
54968      * @param {String} tooltip The new tooltip
54969      */
54970     setColumnTooltip : function(col, tooltip){
54971             this.config[col].tooltip = tooltip;
54972     },
54973
54974     /**
54975      * Returns the dataIndex for the specified column.
54976      * @param {Number} col The column index
54977      * @return {Number}
54978      */
54979     getDataIndex : function(col){
54980         return this.config[col].dataIndex;
54981     },
54982
54983     /**
54984      * Sets the dataIndex for a column.
54985      * @param {Number} col The column index
54986      * @param {Number} dataIndex The new dataIndex
54987      */
54988     setDataIndex : function(col, dataIndex){
54989         this.config[col].dataIndex = dataIndex;
54990     },
54991
54992     
54993     
54994     /**
54995      * Returns true if the cell is editable.
54996      * @param {Number} colIndex The column index
54997      * @param {Number} rowIndex The row index
54998      * @return {Boolean}
54999      */
55000     isCellEditable : function(colIndex, rowIndex){
55001         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55002     },
55003
55004     /**
55005      * Returns the editor defined for the cell/column.
55006      * return false or null to disable editing.
55007      * @param {Number} colIndex The column index
55008      * @param {Number} rowIndex The row index
55009      * @return {Object}
55010      */
55011     getCellEditor : function(colIndex, rowIndex){
55012         return this.config[colIndex].editor;
55013     },
55014
55015     /**
55016      * Sets if a column is editable.
55017      * @param {Number} col The column index
55018      * @param {Boolean} editable True if the column is editable
55019      */
55020     setEditable : function(col, editable){
55021         this.config[col].editable = editable;
55022     },
55023
55024
55025     /**
55026      * Returns true if the column is hidden.
55027      * @param {Number} colIndex The column index
55028      * @return {Boolean}
55029      */
55030     isHidden : function(colIndex){
55031         return this.config[colIndex].hidden;
55032     },
55033
55034
55035     /**
55036      * Returns true if the column width cannot be changed
55037      */
55038     isFixed : function(colIndex){
55039         return this.config[colIndex].fixed;
55040     },
55041
55042     /**
55043      * Returns true if the column can be resized
55044      * @return {Boolean}
55045      */
55046     isResizable : function(colIndex){
55047         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55048     },
55049     /**
55050      * Sets if a column is hidden.
55051      * @param {Number} colIndex The column index
55052      * @param {Boolean} hidden True if the column is hidden
55053      */
55054     setHidden : function(colIndex, hidden){
55055         this.config[colIndex].hidden = hidden;
55056         this.totalWidth = null;
55057         this.fireEvent("hiddenchange", this, colIndex, hidden);
55058     },
55059
55060     /**
55061      * Sets the editor for a column.
55062      * @param {Number} col The column index
55063      * @param {Object} editor The editor object
55064      */
55065     setEditor : function(col, editor){
55066         this.config[col].editor = editor;
55067     }
55068 });
55069
55070 Roo.grid.ColumnModel.defaultRenderer = function(value){
55071         if(typeof value == "string" && value.length < 1){
55072             return "&#160;";
55073         }
55074         return value;
55075 };
55076
55077 // Alias for backwards compatibility
55078 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55079 /*
55080  * Based on:
55081  * Ext JS Library 1.1.1
55082  * Copyright(c) 2006-2007, Ext JS, LLC.
55083  *
55084  * Originally Released Under LGPL - original licence link has changed is not relivant.
55085  *
55086  * Fork - LGPL
55087  * <script type="text/javascript">
55088  */
55089
55090 /**
55091  * @class Roo.grid.AbstractSelectionModel
55092  * @extends Roo.util.Observable
55093  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55094  * implemented by descendant classes.  This class should not be directly instantiated.
55095  * @constructor
55096  */
55097 Roo.grid.AbstractSelectionModel = function(){
55098     this.locked = false;
55099     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55100 };
55101
55102 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55103     /** @ignore Called by the grid automatically. Do not call directly. */
55104     init : function(grid){
55105         this.grid = grid;
55106         this.initEvents();
55107     },
55108
55109     /**
55110      * Locks the selections.
55111      */
55112     lock : function(){
55113         this.locked = true;
55114     },
55115
55116     /**
55117      * Unlocks the selections.
55118      */
55119     unlock : function(){
55120         this.locked = false;
55121     },
55122
55123     /**
55124      * Returns true if the selections are locked.
55125      * @return {Boolean}
55126      */
55127     isLocked : function(){
55128         return this.locked;
55129     }
55130 });/*
55131  * Based on:
55132  * Ext JS Library 1.1.1
55133  * Copyright(c) 2006-2007, Ext JS, LLC.
55134  *
55135  * Originally Released Under LGPL - original licence link has changed is not relivant.
55136  *
55137  * Fork - LGPL
55138  * <script type="text/javascript">
55139  */
55140 /**
55141  * @extends Roo.grid.AbstractSelectionModel
55142  * @class Roo.grid.RowSelectionModel
55143  * The default SelectionModel used by {@link Roo.grid.Grid}.
55144  * It supports multiple selections and keyboard selection/navigation. 
55145  * @constructor
55146  * @param {Object} config
55147  */
55148 Roo.grid.RowSelectionModel = function(config){
55149     Roo.apply(this, config);
55150     this.selections = new Roo.util.MixedCollection(false, function(o){
55151         return o.id;
55152     });
55153
55154     this.last = false;
55155     this.lastActive = false;
55156
55157     this.addEvents({
55158         /**
55159              * @event selectionchange
55160              * Fires when the selection changes
55161              * @param {SelectionModel} this
55162              */
55163             "selectionchange" : true,
55164         /**
55165              * @event afterselectionchange
55166              * Fires after the selection changes (eg. by key press or clicking)
55167              * @param {SelectionModel} this
55168              */
55169             "afterselectionchange" : true,
55170         /**
55171              * @event beforerowselect
55172              * Fires when a row is selected being selected, return false to cancel.
55173              * @param {SelectionModel} this
55174              * @param {Number} rowIndex The selected index
55175              * @param {Boolean} keepExisting False if other selections will be cleared
55176              */
55177             "beforerowselect" : true,
55178         /**
55179              * @event rowselect
55180              * Fires when a row is selected.
55181              * @param {SelectionModel} this
55182              * @param {Number} rowIndex The selected index
55183              * @param {Roo.data.Record} r The record
55184              */
55185             "rowselect" : true,
55186         /**
55187              * @event rowdeselect
55188              * Fires when a row is deselected.
55189              * @param {SelectionModel} this
55190              * @param {Number} rowIndex The selected index
55191              */
55192         "rowdeselect" : true
55193     });
55194     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55195     this.locked = false;
55196 };
55197
55198 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55199     /**
55200      * @cfg {Boolean} singleSelect
55201      * True to allow selection of only one row at a time (defaults to false)
55202      */
55203     singleSelect : false,
55204
55205     // private
55206     initEvents : function(){
55207
55208         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55209             this.grid.on("mousedown", this.handleMouseDown, this);
55210         }else{ // allow click to work like normal
55211             this.grid.on("rowclick", this.handleDragableRowClick, this);
55212         }
55213
55214         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55215             "up" : function(e){
55216                 if(!e.shiftKey){
55217                     this.selectPrevious(e.shiftKey);
55218                 }else if(this.last !== false && this.lastActive !== false){
55219                     var last = this.last;
55220                     this.selectRange(this.last,  this.lastActive-1);
55221                     this.grid.getView().focusRow(this.lastActive);
55222                     if(last !== false){
55223                         this.last = last;
55224                     }
55225                 }else{
55226                     this.selectFirstRow();
55227                 }
55228                 this.fireEvent("afterselectionchange", this);
55229             },
55230             "down" : function(e){
55231                 if(!e.shiftKey){
55232                     this.selectNext(e.shiftKey);
55233                 }else if(this.last !== false && this.lastActive !== false){
55234                     var last = this.last;
55235                     this.selectRange(this.last,  this.lastActive+1);
55236                     this.grid.getView().focusRow(this.lastActive);
55237                     if(last !== false){
55238                         this.last = last;
55239                     }
55240                 }else{
55241                     this.selectFirstRow();
55242                 }
55243                 this.fireEvent("afterselectionchange", this);
55244             },
55245             scope: this
55246         });
55247
55248         var view = this.grid.view;
55249         view.on("refresh", this.onRefresh, this);
55250         view.on("rowupdated", this.onRowUpdated, this);
55251         view.on("rowremoved", this.onRemove, this);
55252     },
55253
55254     // private
55255     onRefresh : function(){
55256         var ds = this.grid.dataSource, i, v = this.grid.view;
55257         var s = this.selections;
55258         s.each(function(r){
55259             if((i = ds.indexOfId(r.id)) != -1){
55260                 v.onRowSelect(i);
55261             }else{
55262                 s.remove(r);
55263             }
55264         });
55265     },
55266
55267     // private
55268     onRemove : function(v, index, r){
55269         this.selections.remove(r);
55270     },
55271
55272     // private
55273     onRowUpdated : function(v, index, r){
55274         if(this.isSelected(r)){
55275             v.onRowSelect(index);
55276         }
55277     },
55278
55279     /**
55280      * Select records.
55281      * @param {Array} records The records to select
55282      * @param {Boolean} keepExisting (optional) True to keep existing selections
55283      */
55284     selectRecords : function(records, keepExisting){
55285         if(!keepExisting){
55286             this.clearSelections();
55287         }
55288         var ds = this.grid.dataSource;
55289         for(var i = 0, len = records.length; i < len; i++){
55290             this.selectRow(ds.indexOf(records[i]), true);
55291         }
55292     },
55293
55294     /**
55295      * Gets the number of selected rows.
55296      * @return {Number}
55297      */
55298     getCount : function(){
55299         return this.selections.length;
55300     },
55301
55302     /**
55303      * Selects the first row in the grid.
55304      */
55305     selectFirstRow : function(){
55306         this.selectRow(0);
55307     },
55308
55309     /**
55310      * Select the last row.
55311      * @param {Boolean} keepExisting (optional) True to keep existing selections
55312      */
55313     selectLastRow : function(keepExisting){
55314         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55315     },
55316
55317     /**
55318      * Selects the row immediately following the last selected row.
55319      * @param {Boolean} keepExisting (optional) True to keep existing selections
55320      */
55321     selectNext : function(keepExisting){
55322         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55323             this.selectRow(this.last+1, keepExisting);
55324             this.grid.getView().focusRow(this.last);
55325         }
55326     },
55327
55328     /**
55329      * Selects the row that precedes the last selected row.
55330      * @param {Boolean} keepExisting (optional) True to keep existing selections
55331      */
55332     selectPrevious : function(keepExisting){
55333         if(this.last){
55334             this.selectRow(this.last-1, keepExisting);
55335             this.grid.getView().focusRow(this.last);
55336         }
55337     },
55338
55339     /**
55340      * Returns the selected records
55341      * @return {Array} Array of selected records
55342      */
55343     getSelections : function(){
55344         return [].concat(this.selections.items);
55345     },
55346
55347     /**
55348      * Returns the first selected record.
55349      * @return {Record}
55350      */
55351     getSelected : function(){
55352         return this.selections.itemAt(0);
55353     },
55354
55355
55356     /**
55357      * Clears all selections.
55358      */
55359     clearSelections : function(fast){
55360         if(this.locked) return;
55361         if(fast !== true){
55362             var ds = this.grid.dataSource;
55363             var s = this.selections;
55364             s.each(function(r){
55365                 this.deselectRow(ds.indexOfId(r.id));
55366             }, this);
55367             s.clear();
55368         }else{
55369             this.selections.clear();
55370         }
55371         this.last = false;
55372     },
55373
55374
55375     /**
55376      * Selects all rows.
55377      */
55378     selectAll : function(){
55379         if(this.locked) return;
55380         this.selections.clear();
55381         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55382             this.selectRow(i, true);
55383         }
55384     },
55385
55386     /**
55387      * Returns True if there is a selection.
55388      * @return {Boolean}
55389      */
55390     hasSelection : function(){
55391         return this.selections.length > 0;
55392     },
55393
55394     /**
55395      * Returns True if the specified row is selected.
55396      * @param {Number/Record} record The record or index of the record to check
55397      * @return {Boolean}
55398      */
55399     isSelected : function(index){
55400         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55401         return (r && this.selections.key(r.id) ? true : false);
55402     },
55403
55404     /**
55405      * Returns True if the specified record id is selected.
55406      * @param {String} id The id of record to check
55407      * @return {Boolean}
55408      */
55409     isIdSelected : function(id){
55410         return (this.selections.key(id) ? true : false);
55411     },
55412
55413     // private
55414     handleMouseDown : function(e, t){
55415         var view = this.grid.getView(), rowIndex;
55416         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55417             return;
55418         };
55419         if(e.shiftKey && this.last !== false){
55420             var last = this.last;
55421             this.selectRange(last, rowIndex, e.ctrlKey);
55422             this.last = last; // reset the last
55423             view.focusRow(rowIndex);
55424         }else{
55425             var isSelected = this.isSelected(rowIndex);
55426             if(e.button !== 0 && isSelected){
55427                 view.focusRow(rowIndex);
55428             }else if(e.ctrlKey && isSelected){
55429                 this.deselectRow(rowIndex);
55430             }else if(!isSelected){
55431                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55432                 view.focusRow(rowIndex);
55433             }
55434         }
55435         this.fireEvent("afterselectionchange", this);
55436     },
55437     // private
55438     handleDragableRowClick :  function(grid, rowIndex, e) 
55439     {
55440         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55441             this.selectRow(rowIndex, false);
55442             grid.view.focusRow(rowIndex);
55443              this.fireEvent("afterselectionchange", this);
55444         }
55445     },
55446     
55447     /**
55448      * Selects multiple rows.
55449      * @param {Array} rows Array of the indexes of the row to select
55450      * @param {Boolean} keepExisting (optional) True to keep existing selections
55451      */
55452     selectRows : function(rows, keepExisting){
55453         if(!keepExisting){
55454             this.clearSelections();
55455         }
55456         for(var i = 0, len = rows.length; i < len; i++){
55457             this.selectRow(rows[i], true);
55458         }
55459     },
55460
55461     /**
55462      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55463      * @param {Number} startRow The index of the first row in the range
55464      * @param {Number} endRow The index of the last row in the range
55465      * @param {Boolean} keepExisting (optional) True to retain existing selections
55466      */
55467     selectRange : function(startRow, endRow, keepExisting){
55468         if(this.locked) return;
55469         if(!keepExisting){
55470             this.clearSelections();
55471         }
55472         if(startRow <= endRow){
55473             for(var i = startRow; i <= endRow; i++){
55474                 this.selectRow(i, true);
55475             }
55476         }else{
55477             for(var i = startRow; i >= endRow; i--){
55478                 this.selectRow(i, true);
55479             }
55480         }
55481     },
55482
55483     /**
55484      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55485      * @param {Number} startRow The index of the first row in the range
55486      * @param {Number} endRow The index of the last row in the range
55487      */
55488     deselectRange : function(startRow, endRow, preventViewNotify){
55489         if(this.locked) return;
55490         for(var i = startRow; i <= endRow; i++){
55491             this.deselectRow(i, preventViewNotify);
55492         }
55493     },
55494
55495     /**
55496      * Selects a row.
55497      * @param {Number} row The index of the row to select
55498      * @param {Boolean} keepExisting (optional) True to keep existing selections
55499      */
55500     selectRow : function(index, keepExisting, preventViewNotify){
55501         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55502         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55503             if(!keepExisting || this.singleSelect){
55504                 this.clearSelections();
55505             }
55506             var r = this.grid.dataSource.getAt(index);
55507             this.selections.add(r);
55508             this.last = this.lastActive = index;
55509             if(!preventViewNotify){
55510                 this.grid.getView().onRowSelect(index);
55511             }
55512             this.fireEvent("rowselect", this, index, r);
55513             this.fireEvent("selectionchange", this);
55514         }
55515     },
55516
55517     /**
55518      * Deselects a row.
55519      * @param {Number} row The index of the row to deselect
55520      */
55521     deselectRow : function(index, preventViewNotify){
55522         if(this.locked) return;
55523         if(this.last == index){
55524             this.last = false;
55525         }
55526         if(this.lastActive == index){
55527             this.lastActive = false;
55528         }
55529         var r = this.grid.dataSource.getAt(index);
55530         this.selections.remove(r);
55531         if(!preventViewNotify){
55532             this.grid.getView().onRowDeselect(index);
55533         }
55534         this.fireEvent("rowdeselect", this, index);
55535         this.fireEvent("selectionchange", this);
55536     },
55537
55538     // private
55539     restoreLast : function(){
55540         if(this._last){
55541             this.last = this._last;
55542         }
55543     },
55544
55545     // private
55546     acceptsNav : function(row, col, cm){
55547         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55548     },
55549
55550     // private
55551     onEditorKey : function(field, e){
55552         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55553         if(k == e.TAB){
55554             e.stopEvent();
55555             ed.completeEdit();
55556             if(e.shiftKey){
55557                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55558             }else{
55559                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55560             }
55561         }else if(k == e.ENTER && !e.ctrlKey){
55562             e.stopEvent();
55563             ed.completeEdit();
55564             if(e.shiftKey){
55565                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55566             }else{
55567                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55568             }
55569         }else if(k == e.ESC){
55570             ed.cancelEdit();
55571         }
55572         if(newCell){
55573             g.startEditing(newCell[0], newCell[1]);
55574         }
55575     }
55576 });/*
55577  * Based on:
55578  * Ext JS Library 1.1.1
55579  * Copyright(c) 2006-2007, Ext JS, LLC.
55580  *
55581  * Originally Released Under LGPL - original licence link has changed is not relivant.
55582  *
55583  * Fork - LGPL
55584  * <script type="text/javascript">
55585  */
55586 /**
55587  * @class Roo.grid.CellSelectionModel
55588  * @extends Roo.grid.AbstractSelectionModel
55589  * This class provides the basic implementation for cell selection in a grid.
55590  * @constructor
55591  * @param {Object} config The object containing the configuration of this model.
55592  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55593  */
55594 Roo.grid.CellSelectionModel = function(config){
55595     Roo.apply(this, config);
55596
55597     this.selection = null;
55598
55599     this.addEvents({
55600         /**
55601              * @event beforerowselect
55602              * Fires before a cell is selected.
55603              * @param {SelectionModel} this
55604              * @param {Number} rowIndex The selected row index
55605              * @param {Number} colIndex The selected cell index
55606              */
55607             "beforecellselect" : true,
55608         /**
55609              * @event cellselect
55610              * Fires when a cell is selected.
55611              * @param {SelectionModel} this
55612              * @param {Number} rowIndex The selected row index
55613              * @param {Number} colIndex The selected cell index
55614              */
55615             "cellselect" : true,
55616         /**
55617              * @event selectionchange
55618              * Fires when the active selection changes.
55619              * @param {SelectionModel} this
55620              * @param {Object} selection null for no selection or an object (o) with two properties
55621                 <ul>
55622                 <li>o.record: the record object for the row the selection is in</li>
55623                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55624                 </ul>
55625              */
55626             "selectionchange" : true,
55627         /**
55628              * @event tabend
55629              * Fires when the tab (or enter) was pressed on the last editable cell
55630              * You can use this to trigger add new row.
55631              * @param {SelectionModel} this
55632              */
55633             "tabend" : true,
55634          /**
55635              * @event beforeeditnext
55636              * Fires before the next editable sell is made active
55637              * You can use this to skip to another cell or fire the tabend
55638              *    if you set cell to false
55639              * @param {Object} eventdata object : { cell : [ row, col ] } 
55640              */
55641             "beforeeditnext" : true
55642     });
55643     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55644 };
55645
55646 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55647     
55648     enter_is_tab: false,
55649
55650     /** @ignore */
55651     initEvents : function(){
55652         this.grid.on("mousedown", this.handleMouseDown, this);
55653         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55654         var view = this.grid.view;
55655         view.on("refresh", this.onViewChange, this);
55656         view.on("rowupdated", this.onRowUpdated, this);
55657         view.on("beforerowremoved", this.clearSelections, this);
55658         view.on("beforerowsinserted", this.clearSelections, this);
55659         if(this.grid.isEditor){
55660             this.grid.on("beforeedit", this.beforeEdit,  this);
55661         }
55662     },
55663
55664         //private
55665     beforeEdit : function(e){
55666         this.select(e.row, e.column, false, true, e.record);
55667     },
55668
55669         //private
55670     onRowUpdated : function(v, index, r){
55671         if(this.selection && this.selection.record == r){
55672             v.onCellSelect(index, this.selection.cell[1]);
55673         }
55674     },
55675
55676         //private
55677     onViewChange : function(){
55678         this.clearSelections(true);
55679     },
55680
55681         /**
55682          * Returns the currently selected cell,.
55683          * @return {Array} The selected cell (row, column) or null if none selected.
55684          */
55685     getSelectedCell : function(){
55686         return this.selection ? this.selection.cell : null;
55687     },
55688
55689     /**
55690      * Clears all selections.
55691      * @param {Boolean} true to prevent the gridview from being notified about the change.
55692      */
55693     clearSelections : function(preventNotify){
55694         var s = this.selection;
55695         if(s){
55696             if(preventNotify !== true){
55697                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55698             }
55699             this.selection = null;
55700             this.fireEvent("selectionchange", this, null);
55701         }
55702     },
55703
55704     /**
55705      * Returns true if there is a selection.
55706      * @return {Boolean}
55707      */
55708     hasSelection : function(){
55709         return this.selection ? true : false;
55710     },
55711
55712     /** @ignore */
55713     handleMouseDown : function(e, t){
55714         var v = this.grid.getView();
55715         if(this.isLocked()){
55716             return;
55717         };
55718         var row = v.findRowIndex(t);
55719         var cell = v.findCellIndex(t);
55720         if(row !== false && cell !== false){
55721             this.select(row, cell);
55722         }
55723     },
55724
55725     /**
55726      * Selects a cell.
55727      * @param {Number} rowIndex
55728      * @param {Number} collIndex
55729      */
55730     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55731         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55732             this.clearSelections();
55733             r = r || this.grid.dataSource.getAt(rowIndex);
55734             this.selection = {
55735                 record : r,
55736                 cell : [rowIndex, colIndex]
55737             };
55738             if(!preventViewNotify){
55739                 var v = this.grid.getView();
55740                 v.onCellSelect(rowIndex, colIndex);
55741                 if(preventFocus !== true){
55742                     v.focusCell(rowIndex, colIndex);
55743                 }
55744             }
55745             this.fireEvent("cellselect", this, rowIndex, colIndex);
55746             this.fireEvent("selectionchange", this, this.selection);
55747         }
55748     },
55749
55750         //private
55751     isSelectable : function(rowIndex, colIndex, cm){
55752         return !cm.isHidden(colIndex);
55753     },
55754
55755     /** @ignore */
55756     handleKeyDown : function(e){
55757         //Roo.log('Cell Sel Model handleKeyDown');
55758         if(!e.isNavKeyPress()){
55759             return;
55760         }
55761         var g = this.grid, s = this.selection;
55762         if(!s){
55763             e.stopEvent();
55764             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55765             if(cell){
55766                 this.select(cell[0], cell[1]);
55767             }
55768             return;
55769         }
55770         var sm = this;
55771         var walk = function(row, col, step){
55772             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55773         };
55774         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55775         var newCell;
55776
55777       
55778
55779         switch(k){
55780             case e.TAB:
55781                 // handled by onEditorKey
55782                 if (g.isEditor && g.editing) {
55783                     return;
55784                 }
55785                 if(e.shiftKey) {
55786                     newCell = walk(r, c-1, -1);
55787                 } else {
55788                     newCell = walk(r, c+1, 1);
55789                 }
55790                 break;
55791             
55792             case e.DOWN:
55793                newCell = walk(r+1, c, 1);
55794                 break;
55795             
55796             case e.UP:
55797                 newCell = walk(r-1, c, -1);
55798                 break;
55799             
55800             case e.RIGHT:
55801                 newCell = walk(r, c+1, 1);
55802                 break;
55803             
55804             case e.LEFT:
55805                 newCell = walk(r, c-1, -1);
55806                 break;
55807             
55808             case e.ENTER:
55809                 
55810                 if(g.isEditor && !g.editing){
55811                    g.startEditing(r, c);
55812                    e.stopEvent();
55813                    return;
55814                 }
55815                 
55816                 
55817              break;
55818         };
55819         if(newCell){
55820             this.select(newCell[0], newCell[1]);
55821             e.stopEvent();
55822             
55823         }
55824     },
55825
55826     acceptsNav : function(row, col, cm){
55827         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55828     },
55829     /**
55830      * Selects a cell.
55831      * @param {Number} field (not used) - as it's normally used as a listener
55832      * @param {Number} e - event - fake it by using
55833      *
55834      * var e = Roo.EventObjectImpl.prototype;
55835      * e.keyCode = e.TAB
55836      *
55837      * 
55838      */
55839     onEditorKey : function(field, e){
55840         
55841         var k = e.getKey(),
55842             newCell,
55843             g = this.grid,
55844             ed = g.activeEditor,
55845             forward = false;
55846         ///Roo.log('onEditorKey' + k);
55847         
55848         
55849         if (this.enter_is_tab && k == e.ENTER) {
55850             k = e.TAB;
55851         }
55852         
55853         if(k == e.TAB){
55854             if(e.shiftKey){
55855                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55856             }else{
55857                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55858                 forward = true;
55859             }
55860             
55861             e.stopEvent();
55862             
55863         } else if(k == e.ENTER &&  !e.ctrlKey){
55864             ed.completeEdit();
55865             e.stopEvent();
55866             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55867         
55868                 } else if(k == e.ESC){
55869             ed.cancelEdit();
55870         }
55871                 
55872         if (newCell) {
55873             var ecall = { cell : newCell, forward : forward };
55874             this.fireEvent('beforeeditnext', ecall );
55875             newCell = ecall.cell;
55876                         forward = ecall.forward;
55877         }
55878                 
55879         if(newCell){
55880             //Roo.log('next cell after edit');
55881             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55882         } else if (forward) {
55883             // tabbed past last
55884             this.fireEvent.defer(100, this, ['tabend',this]);
55885         }
55886     }
55887 });/*
55888  * Based on:
55889  * Ext JS Library 1.1.1
55890  * Copyright(c) 2006-2007, Ext JS, LLC.
55891  *
55892  * Originally Released Under LGPL - original licence link has changed is not relivant.
55893  *
55894  * Fork - LGPL
55895  * <script type="text/javascript">
55896  */
55897  
55898 /**
55899  * @class Roo.grid.EditorGrid
55900  * @extends Roo.grid.Grid
55901  * Class for creating and editable grid.
55902  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55903  * The container MUST have some type of size defined for the grid to fill. The container will be 
55904  * automatically set to position relative if it isn't already.
55905  * @param {Object} dataSource The data model to bind to
55906  * @param {Object} colModel The column model with info about this grid's columns
55907  */
55908 Roo.grid.EditorGrid = function(container, config){
55909     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55910     this.getGridEl().addClass("xedit-grid");
55911
55912     if(!this.selModel){
55913         this.selModel = new Roo.grid.CellSelectionModel();
55914     }
55915
55916     this.activeEditor = null;
55917
55918         this.addEvents({
55919             /**
55920              * @event beforeedit
55921              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55922              * <ul style="padding:5px;padding-left:16px;">
55923              * <li>grid - This grid</li>
55924              * <li>record - The record being edited</li>
55925              * <li>field - The field name being edited</li>
55926              * <li>value - The value for the field being edited.</li>
55927              * <li>row - The grid row index</li>
55928              * <li>column - The grid column index</li>
55929              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55930              * </ul>
55931              * @param {Object} e An edit event (see above for description)
55932              */
55933             "beforeedit" : true,
55934             /**
55935              * @event afteredit
55936              * Fires after a cell is edited. <br />
55937              * <ul style="padding:5px;padding-left:16px;">
55938              * <li>grid - This grid</li>
55939              * <li>record - The record being edited</li>
55940              * <li>field - The field name being edited</li>
55941              * <li>value - The value being set</li>
55942              * <li>originalValue - The original value for the field, before the edit.</li>
55943              * <li>row - The grid row index</li>
55944              * <li>column - The grid column index</li>
55945              * </ul>
55946              * @param {Object} e An edit event (see above for description)
55947              */
55948             "afteredit" : true,
55949             /**
55950              * @event validateedit
55951              * Fires after a cell is edited, but before the value is set in the record. 
55952          * You can use this to modify the value being set in the field, Return false
55953              * to cancel the change. The edit event object has the following properties <br />
55954              * <ul style="padding:5px;padding-left:16px;">
55955          * <li>editor - This editor</li>
55956              * <li>grid - This grid</li>
55957              * <li>record - The record being edited</li>
55958              * <li>field - The field name being edited</li>
55959              * <li>value - The value being set</li>
55960              * <li>originalValue - The original value for the field, before the edit.</li>
55961              * <li>row - The grid row index</li>
55962              * <li>column - The grid column index</li>
55963              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55964              * </ul>
55965              * @param {Object} e An edit event (see above for description)
55966              */
55967             "validateedit" : true
55968         });
55969     this.on("bodyscroll", this.stopEditing,  this);
55970     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55971 };
55972
55973 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55974     /**
55975      * @cfg {Number} clicksToEdit
55976      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55977      */
55978     clicksToEdit: 2,
55979
55980     // private
55981     isEditor : true,
55982     // private
55983     trackMouseOver: false, // causes very odd FF errors
55984
55985     onCellDblClick : function(g, row, col){
55986         this.startEditing(row, col);
55987     },
55988
55989     onEditComplete : function(ed, value, startValue){
55990         this.editing = false;
55991         this.activeEditor = null;
55992         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55993         var r = ed.record;
55994         var field = this.colModel.getDataIndex(ed.col);
55995         var e = {
55996             grid: this,
55997             record: r,
55998             field: field,
55999             originalValue: startValue,
56000             value: value,
56001             row: ed.row,
56002             column: ed.col,
56003             cancel:false,
56004             editor: ed
56005         };
56006         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56007         cell.show();
56008           
56009         if(String(value) !== String(startValue)){
56010             
56011             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56012                 r.set(field, e.value);
56013                 // if we are dealing with a combo box..
56014                 // then we also set the 'name' colum to be the displayField
56015                 if (ed.field.displayField && ed.field.name) {
56016                     r.set(ed.field.name, ed.field.el.dom.value);
56017                 }
56018                 
56019                 delete e.cancel; //?? why!!!
56020                 this.fireEvent("afteredit", e);
56021             }
56022         } else {
56023             this.fireEvent("afteredit", e); // always fire it!
56024         }
56025         this.view.focusCell(ed.row, ed.col);
56026     },
56027
56028     /**
56029      * Starts editing the specified for the specified row/column
56030      * @param {Number} rowIndex
56031      * @param {Number} colIndex
56032      */
56033     startEditing : function(row, col){
56034         this.stopEditing();
56035         if(this.colModel.isCellEditable(col, row)){
56036             this.view.ensureVisible(row, col, true);
56037           
56038             var r = this.dataSource.getAt(row);
56039             var field = this.colModel.getDataIndex(col);
56040             var cell = Roo.get(this.view.getCell(row,col));
56041             var e = {
56042                 grid: this,
56043                 record: r,
56044                 field: field,
56045                 value: r.data[field],
56046                 row: row,
56047                 column: col,
56048                 cancel:false 
56049             };
56050             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56051                 this.editing = true;
56052                 var ed = this.colModel.getCellEditor(col, row);
56053                 
56054                 if (!ed) {
56055                     return;
56056                 }
56057                 if(!ed.rendered){
56058                     ed.render(ed.parentEl || document.body);
56059                 }
56060                 ed.field.reset();
56061                
56062                 cell.hide();
56063                 
56064                 (function(){ // complex but required for focus issues in safari, ie and opera
56065                     ed.row = row;
56066                     ed.col = col;
56067                     ed.record = r;
56068                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56069                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56070                     this.activeEditor = ed;
56071                     var v = r.data[field];
56072                     ed.startEdit(this.view.getCell(row, col), v);
56073                     // combo's with 'displayField and name set
56074                     if (ed.field.displayField && ed.field.name) {
56075                         ed.field.el.dom.value = r.data[ed.field.name];
56076                     }
56077                     
56078                     
56079                 }).defer(50, this);
56080             }
56081         }
56082     },
56083         
56084     /**
56085      * Stops any active editing
56086      */
56087     stopEditing : function(){
56088         if(this.activeEditor){
56089             this.activeEditor.completeEdit();
56090         }
56091         this.activeEditor = null;
56092     },
56093         
56094          /**
56095      * Called to get grid's drag proxy text, by default returns this.ddText.
56096      * @return {String}
56097      */
56098     getDragDropText : function(){
56099         var count = this.selModel.getSelectedCell() ? 1 : 0;
56100         return String.format(this.ddText, count, count == 1 ? '' : 's');
56101     }
56102         
56103 });/*
56104  * Based on:
56105  * Ext JS Library 1.1.1
56106  * Copyright(c) 2006-2007, Ext JS, LLC.
56107  *
56108  * Originally Released Under LGPL - original licence link has changed is not relivant.
56109  *
56110  * Fork - LGPL
56111  * <script type="text/javascript">
56112  */
56113
56114 // private - not really -- you end up using it !
56115 // This is a support class used internally by the Grid components
56116
56117 /**
56118  * @class Roo.grid.GridEditor
56119  * @extends Roo.Editor
56120  * Class for creating and editable grid elements.
56121  * @param {Object} config any settings (must include field)
56122  */
56123 Roo.grid.GridEditor = function(field, config){
56124     if (!config && field.field) {
56125         config = field;
56126         field = Roo.factory(config.field, Roo.form);
56127     }
56128     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56129     field.monitorTab = false;
56130 };
56131
56132 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56133     
56134     /**
56135      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56136      */
56137     
56138     alignment: "tl-tl",
56139     autoSize: "width",
56140     hideEl : false,
56141     cls: "x-small-editor x-grid-editor",
56142     shim:false,
56143     shadow:"frame"
56144 });/*
56145  * Based on:
56146  * Ext JS Library 1.1.1
56147  * Copyright(c) 2006-2007, Ext JS, LLC.
56148  *
56149  * Originally Released Under LGPL - original licence link has changed is not relivant.
56150  *
56151  * Fork - LGPL
56152  * <script type="text/javascript">
56153  */
56154   
56155
56156   
56157 Roo.grid.PropertyRecord = Roo.data.Record.create([
56158     {name:'name',type:'string'},  'value'
56159 ]);
56160
56161
56162 Roo.grid.PropertyStore = function(grid, source){
56163     this.grid = grid;
56164     this.store = new Roo.data.Store({
56165         recordType : Roo.grid.PropertyRecord
56166     });
56167     this.store.on('update', this.onUpdate,  this);
56168     if(source){
56169         this.setSource(source);
56170     }
56171     Roo.grid.PropertyStore.superclass.constructor.call(this);
56172 };
56173
56174
56175
56176 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56177     setSource : function(o){
56178         this.source = o;
56179         this.store.removeAll();
56180         var data = [];
56181         for(var k in o){
56182             if(this.isEditableValue(o[k])){
56183                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56184             }
56185         }
56186         this.store.loadRecords({records: data}, {}, true);
56187     },
56188
56189     onUpdate : function(ds, record, type){
56190         if(type == Roo.data.Record.EDIT){
56191             var v = record.data['value'];
56192             var oldValue = record.modified['value'];
56193             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56194                 this.source[record.id] = v;
56195                 record.commit();
56196                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56197             }else{
56198                 record.reject();
56199             }
56200         }
56201     },
56202
56203     getProperty : function(row){
56204        return this.store.getAt(row);
56205     },
56206
56207     isEditableValue: function(val){
56208         if(val && val instanceof Date){
56209             return true;
56210         }else if(typeof val == 'object' || typeof val == 'function'){
56211             return false;
56212         }
56213         return true;
56214     },
56215
56216     setValue : function(prop, value){
56217         this.source[prop] = value;
56218         this.store.getById(prop).set('value', value);
56219     },
56220
56221     getSource : function(){
56222         return this.source;
56223     }
56224 });
56225
56226 Roo.grid.PropertyColumnModel = function(grid, store){
56227     this.grid = grid;
56228     var g = Roo.grid;
56229     g.PropertyColumnModel.superclass.constructor.call(this, [
56230         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56231         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56232     ]);
56233     this.store = store;
56234     this.bselect = Roo.DomHelper.append(document.body, {
56235         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56236             {tag: 'option', value: 'true', html: 'true'},
56237             {tag: 'option', value: 'false', html: 'false'}
56238         ]
56239     });
56240     Roo.id(this.bselect);
56241     var f = Roo.form;
56242     this.editors = {
56243         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56244         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56245         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56246         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56247         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56248     };
56249     this.renderCellDelegate = this.renderCell.createDelegate(this);
56250     this.renderPropDelegate = this.renderProp.createDelegate(this);
56251 };
56252
56253 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56254     
56255     
56256     nameText : 'Name',
56257     valueText : 'Value',
56258     
56259     dateFormat : 'm/j/Y',
56260     
56261     
56262     renderDate : function(dateVal){
56263         return dateVal.dateFormat(this.dateFormat);
56264     },
56265
56266     renderBool : function(bVal){
56267         return bVal ? 'true' : 'false';
56268     },
56269
56270     isCellEditable : function(colIndex, rowIndex){
56271         return colIndex == 1;
56272     },
56273
56274     getRenderer : function(col){
56275         return col == 1 ?
56276             this.renderCellDelegate : this.renderPropDelegate;
56277     },
56278
56279     renderProp : function(v){
56280         return this.getPropertyName(v);
56281     },
56282
56283     renderCell : function(val){
56284         var rv = val;
56285         if(val instanceof Date){
56286             rv = this.renderDate(val);
56287         }else if(typeof val == 'boolean'){
56288             rv = this.renderBool(val);
56289         }
56290         return Roo.util.Format.htmlEncode(rv);
56291     },
56292
56293     getPropertyName : function(name){
56294         var pn = this.grid.propertyNames;
56295         return pn && pn[name] ? pn[name] : name;
56296     },
56297
56298     getCellEditor : function(colIndex, rowIndex){
56299         var p = this.store.getProperty(rowIndex);
56300         var n = p.data['name'], val = p.data['value'];
56301         
56302         if(typeof(this.grid.customEditors[n]) == 'string'){
56303             return this.editors[this.grid.customEditors[n]];
56304         }
56305         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56306             return this.grid.customEditors[n];
56307         }
56308         if(val instanceof Date){
56309             return this.editors['date'];
56310         }else if(typeof val == 'number'){
56311             return this.editors['number'];
56312         }else if(typeof val == 'boolean'){
56313             return this.editors['boolean'];
56314         }else{
56315             return this.editors['string'];
56316         }
56317     }
56318 });
56319
56320 /**
56321  * @class Roo.grid.PropertyGrid
56322  * @extends Roo.grid.EditorGrid
56323  * This class represents the  interface of a component based property grid control.
56324  * <br><br>Usage:<pre><code>
56325  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56326       
56327  });
56328  // set any options
56329  grid.render();
56330  * </code></pre>
56331   
56332  * @constructor
56333  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56334  * The container MUST have some type of size defined for the grid to fill. The container will be
56335  * automatically set to position relative if it isn't already.
56336  * @param {Object} config A config object that sets properties on this grid.
56337  */
56338 Roo.grid.PropertyGrid = function(container, config){
56339     config = config || {};
56340     var store = new Roo.grid.PropertyStore(this);
56341     this.store = store;
56342     var cm = new Roo.grid.PropertyColumnModel(this, store);
56343     store.store.sort('name', 'ASC');
56344     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56345         ds: store.store,
56346         cm: cm,
56347         enableColLock:false,
56348         enableColumnMove:false,
56349         stripeRows:false,
56350         trackMouseOver: false,
56351         clicksToEdit:1
56352     }, config));
56353     this.getGridEl().addClass('x-props-grid');
56354     this.lastEditRow = null;
56355     this.on('columnresize', this.onColumnResize, this);
56356     this.addEvents({
56357          /**
56358              * @event beforepropertychange
56359              * Fires before a property changes (return false to stop?)
56360              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56361              * @param {String} id Record Id
56362              * @param {String} newval New Value
56363          * @param {String} oldval Old Value
56364              */
56365         "beforepropertychange": true,
56366         /**
56367              * @event propertychange
56368              * Fires after a property changes
56369              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56370              * @param {String} id Record Id
56371              * @param {String} newval New Value
56372          * @param {String} oldval Old Value
56373              */
56374         "propertychange": true
56375     });
56376     this.customEditors = this.customEditors || {};
56377 };
56378 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56379     
56380      /**
56381      * @cfg {Object} customEditors map of colnames=> custom editors.
56382      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56383      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56384      * false disables editing of the field.
56385          */
56386     
56387       /**
56388      * @cfg {Object} propertyNames map of property Names to their displayed value
56389          */
56390     
56391     render : function(){
56392         Roo.grid.PropertyGrid.superclass.render.call(this);
56393         this.autoSize.defer(100, this);
56394     },
56395
56396     autoSize : function(){
56397         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56398         if(this.view){
56399             this.view.fitColumns();
56400         }
56401     },
56402
56403     onColumnResize : function(){
56404         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56405         this.autoSize();
56406     },
56407     /**
56408      * Sets the data for the Grid
56409      * accepts a Key => Value object of all the elements avaiable.
56410      * @param {Object} data  to appear in grid.
56411      */
56412     setSource : function(source){
56413         this.store.setSource(source);
56414         //this.autoSize();
56415     },
56416     /**
56417      * Gets all the data from the grid.
56418      * @return {Object} data  data stored in grid
56419      */
56420     getSource : function(){
56421         return this.store.getSource();
56422     }
56423 });/*
56424   
56425  * Licence LGPL
56426  
56427  */
56428  
56429 /**
56430  * @class Roo.grid.Calendar
56431  * @extends Roo.util.Grid
56432  * This class extends the Grid to provide a calendar widget
56433  * <br><br>Usage:<pre><code>
56434  var grid = new Roo.grid.Calendar("my-container-id", {
56435      ds: myDataStore,
56436      cm: myColModel,
56437      selModel: mySelectionModel,
56438      autoSizeColumns: true,
56439      monitorWindowResize: false,
56440      trackMouseOver: true
56441      eventstore : real data store..
56442  });
56443  // set any options
56444  grid.render();
56445   
56446   * @constructor
56447  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56448  * The container MUST have some type of size defined for the grid to fill. The container will be
56449  * automatically set to position relative if it isn't already.
56450  * @param {Object} config A config object that sets properties on this grid.
56451  */
56452 Roo.grid.Calendar = function(container, config){
56453         // initialize the container
56454         this.container = Roo.get(container);
56455         this.container.update("");
56456         this.container.setStyle("overflow", "hidden");
56457     this.container.addClass('x-grid-container');
56458
56459     this.id = this.container.id;
56460
56461     Roo.apply(this, config);
56462     // check and correct shorthanded configs
56463     
56464     var rows = [];
56465     var d =1;
56466     for (var r = 0;r < 6;r++) {
56467         
56468         rows[r]=[];
56469         for (var c =0;c < 7;c++) {
56470             rows[r][c]= '';
56471         }
56472     }
56473     if (this.eventStore) {
56474         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56475         this.eventStore.on('load',this.onLoad, this);
56476         this.eventStore.on('beforeload',this.clearEvents, this);
56477          
56478     }
56479     
56480     this.dataSource = new Roo.data.Store({
56481             proxy: new Roo.data.MemoryProxy(rows),
56482             reader: new Roo.data.ArrayReader({}, [
56483                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56484     });
56485
56486     this.dataSource.load();
56487     this.ds = this.dataSource;
56488     this.ds.xmodule = this.xmodule || false;
56489     
56490     
56491     var cellRender = function(v,x,r)
56492     {
56493         return String.format(
56494             '<div class="fc-day  fc-widget-content"><div>' +
56495                 '<div class="fc-event-container"></div>' +
56496                 '<div class="fc-day-number">{0}</div>'+
56497                 
56498                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56499             '</div></div>', v);
56500     
56501     }
56502     
56503     
56504     this.colModel = new Roo.grid.ColumnModel( [
56505         {
56506             xtype: 'ColumnModel',
56507             xns: Roo.grid,
56508             dataIndex : 'weekday0',
56509             header : 'Sunday',
56510             renderer : cellRender
56511         },
56512         {
56513             xtype: 'ColumnModel',
56514             xns: Roo.grid,
56515             dataIndex : 'weekday1',
56516             header : 'Monday',
56517             renderer : cellRender
56518         },
56519         {
56520             xtype: 'ColumnModel',
56521             xns: Roo.grid,
56522             dataIndex : 'weekday2',
56523             header : 'Tuesday',
56524             renderer : cellRender
56525         },
56526         {
56527             xtype: 'ColumnModel',
56528             xns: Roo.grid,
56529             dataIndex : 'weekday3',
56530             header : 'Wednesday',
56531             renderer : cellRender
56532         },
56533         {
56534             xtype: 'ColumnModel',
56535             xns: Roo.grid,
56536             dataIndex : 'weekday4',
56537             header : 'Thursday',
56538             renderer : cellRender
56539         },
56540         {
56541             xtype: 'ColumnModel',
56542             xns: Roo.grid,
56543             dataIndex : 'weekday5',
56544             header : 'Friday',
56545             renderer : cellRender
56546         },
56547         {
56548             xtype: 'ColumnModel',
56549             xns: Roo.grid,
56550             dataIndex : 'weekday6',
56551             header : 'Saturday',
56552             renderer : cellRender
56553         }
56554     ]);
56555     this.cm = this.colModel;
56556     this.cm.xmodule = this.xmodule || false;
56557  
56558         
56559           
56560     //this.selModel = new Roo.grid.CellSelectionModel();
56561     //this.sm = this.selModel;
56562     //this.selModel.init(this);
56563     
56564     
56565     if(this.width){
56566         this.container.setWidth(this.width);
56567     }
56568
56569     if(this.height){
56570         this.container.setHeight(this.height);
56571     }
56572     /** @private */
56573         this.addEvents({
56574         // raw events
56575         /**
56576          * @event click
56577          * The raw click event for the entire grid.
56578          * @param {Roo.EventObject} e
56579          */
56580         "click" : true,
56581         /**
56582          * @event dblclick
56583          * The raw dblclick event for the entire grid.
56584          * @param {Roo.EventObject} e
56585          */
56586         "dblclick" : true,
56587         /**
56588          * @event contextmenu
56589          * The raw contextmenu event for the entire grid.
56590          * @param {Roo.EventObject} e
56591          */
56592         "contextmenu" : true,
56593         /**
56594          * @event mousedown
56595          * The raw mousedown event for the entire grid.
56596          * @param {Roo.EventObject} e
56597          */
56598         "mousedown" : true,
56599         /**
56600          * @event mouseup
56601          * The raw mouseup event for the entire grid.
56602          * @param {Roo.EventObject} e
56603          */
56604         "mouseup" : true,
56605         /**
56606          * @event mouseover
56607          * The raw mouseover event for the entire grid.
56608          * @param {Roo.EventObject} e
56609          */
56610         "mouseover" : true,
56611         /**
56612          * @event mouseout
56613          * The raw mouseout event for the entire grid.
56614          * @param {Roo.EventObject} e
56615          */
56616         "mouseout" : true,
56617         /**
56618          * @event keypress
56619          * The raw keypress event for the entire grid.
56620          * @param {Roo.EventObject} e
56621          */
56622         "keypress" : true,
56623         /**
56624          * @event keydown
56625          * The raw keydown event for the entire grid.
56626          * @param {Roo.EventObject} e
56627          */
56628         "keydown" : true,
56629
56630         // custom events
56631
56632         /**
56633          * @event cellclick
56634          * Fires when a cell is clicked
56635          * @param {Grid} this
56636          * @param {Number} rowIndex
56637          * @param {Number} columnIndex
56638          * @param {Roo.EventObject} e
56639          */
56640         "cellclick" : true,
56641         /**
56642          * @event celldblclick
56643          * Fires when a cell is double clicked
56644          * @param {Grid} this
56645          * @param {Number} rowIndex
56646          * @param {Number} columnIndex
56647          * @param {Roo.EventObject} e
56648          */
56649         "celldblclick" : true,
56650         /**
56651          * @event rowclick
56652          * Fires when a row is clicked
56653          * @param {Grid} this
56654          * @param {Number} rowIndex
56655          * @param {Roo.EventObject} e
56656          */
56657         "rowclick" : true,
56658         /**
56659          * @event rowdblclick
56660          * Fires when a row is double clicked
56661          * @param {Grid} this
56662          * @param {Number} rowIndex
56663          * @param {Roo.EventObject} e
56664          */
56665         "rowdblclick" : true,
56666         /**
56667          * @event headerclick
56668          * Fires when a header is clicked
56669          * @param {Grid} this
56670          * @param {Number} columnIndex
56671          * @param {Roo.EventObject} e
56672          */
56673         "headerclick" : true,
56674         /**
56675          * @event headerdblclick
56676          * Fires when a header cell is double clicked
56677          * @param {Grid} this
56678          * @param {Number} columnIndex
56679          * @param {Roo.EventObject} e
56680          */
56681         "headerdblclick" : true,
56682         /**
56683          * @event rowcontextmenu
56684          * Fires when a row is right clicked
56685          * @param {Grid} this
56686          * @param {Number} rowIndex
56687          * @param {Roo.EventObject} e
56688          */
56689         "rowcontextmenu" : true,
56690         /**
56691          * @event cellcontextmenu
56692          * Fires when a cell is right clicked
56693          * @param {Grid} this
56694          * @param {Number} rowIndex
56695          * @param {Number} cellIndex
56696          * @param {Roo.EventObject} e
56697          */
56698          "cellcontextmenu" : true,
56699         /**
56700          * @event headercontextmenu
56701          * Fires when a header is right clicked
56702          * @param {Grid} this
56703          * @param {Number} columnIndex
56704          * @param {Roo.EventObject} e
56705          */
56706         "headercontextmenu" : true,
56707         /**
56708          * @event bodyscroll
56709          * Fires when the body element is scrolled
56710          * @param {Number} scrollLeft
56711          * @param {Number} scrollTop
56712          */
56713         "bodyscroll" : true,
56714         /**
56715          * @event columnresize
56716          * Fires when the user resizes a column
56717          * @param {Number} columnIndex
56718          * @param {Number} newSize
56719          */
56720         "columnresize" : true,
56721         /**
56722          * @event columnmove
56723          * Fires when the user moves a column
56724          * @param {Number} oldIndex
56725          * @param {Number} newIndex
56726          */
56727         "columnmove" : true,
56728         /**
56729          * @event startdrag
56730          * Fires when row(s) start being dragged
56731          * @param {Grid} this
56732          * @param {Roo.GridDD} dd The drag drop object
56733          * @param {event} e The raw browser event
56734          */
56735         "startdrag" : true,
56736         /**
56737          * @event enddrag
56738          * Fires when a drag operation is complete
56739          * @param {Grid} this
56740          * @param {Roo.GridDD} dd The drag drop object
56741          * @param {event} e The raw browser event
56742          */
56743         "enddrag" : true,
56744         /**
56745          * @event dragdrop
56746          * Fires when dragged row(s) are dropped on a valid DD target
56747          * @param {Grid} this
56748          * @param {Roo.GridDD} dd The drag drop object
56749          * @param {String} targetId The target drag drop object
56750          * @param {event} e The raw browser event
56751          */
56752         "dragdrop" : true,
56753         /**
56754          * @event dragover
56755          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56756          * @param {Grid} this
56757          * @param {Roo.GridDD} dd The drag drop object
56758          * @param {String} targetId The target drag drop object
56759          * @param {event} e The raw browser event
56760          */
56761         "dragover" : true,
56762         /**
56763          * @event dragenter
56764          *  Fires when the dragged row(s) first cross another DD target while being dragged
56765          * @param {Grid} this
56766          * @param {Roo.GridDD} dd The drag drop object
56767          * @param {String} targetId The target drag drop object
56768          * @param {event} e The raw browser event
56769          */
56770         "dragenter" : true,
56771         /**
56772          * @event dragout
56773          * Fires when the dragged row(s) leave another DD target while being dragged
56774          * @param {Grid} this
56775          * @param {Roo.GridDD} dd The drag drop object
56776          * @param {String} targetId The target drag drop object
56777          * @param {event} e The raw browser event
56778          */
56779         "dragout" : true,
56780         /**
56781          * @event rowclass
56782          * Fires when a row is rendered, so you can change add a style to it.
56783          * @param {GridView} gridview   The grid view
56784          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56785          */
56786         'rowclass' : true,
56787
56788         /**
56789          * @event render
56790          * Fires when the grid is rendered
56791          * @param {Grid} grid
56792          */
56793         'render' : true,
56794             /**
56795              * @event select
56796              * Fires when a date is selected
56797              * @param {DatePicker} this
56798              * @param {Date} date The selected date
56799              */
56800         'select': true,
56801         /**
56802              * @event monthchange
56803              * Fires when the displayed month changes 
56804              * @param {DatePicker} this
56805              * @param {Date} date The selected month
56806              */
56807         'monthchange': true,
56808         /**
56809              * @event evententer
56810              * Fires when mouse over an event
56811              * @param {Calendar} this
56812              * @param {event} Event
56813              */
56814         'evententer': true,
56815         /**
56816              * @event eventleave
56817              * Fires when the mouse leaves an
56818              * @param {Calendar} this
56819              * @param {event}
56820              */
56821         'eventleave': true,
56822         /**
56823              * @event eventclick
56824              * Fires when the mouse click an
56825              * @param {Calendar} this
56826              * @param {event}
56827              */
56828         'eventclick': true,
56829         /**
56830              * @event eventrender
56831              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56832              * @param {Calendar} this
56833              * @param {data} data to be modified
56834              */
56835         'eventrender': true
56836         
56837     });
56838
56839     Roo.grid.Grid.superclass.constructor.call(this);
56840     this.on('render', function() {
56841         this.view.el.addClass('x-grid-cal'); 
56842         
56843         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56844
56845     },this);
56846     
56847     if (!Roo.grid.Calendar.style) {
56848         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56849             
56850             
56851             '.x-grid-cal .x-grid-col' :  {
56852                 height: 'auto !important',
56853                 'vertical-align': 'top'
56854             },
56855             '.x-grid-cal  .fc-event-hori' : {
56856                 height: '14px'
56857             }
56858              
56859             
56860         }, Roo.id());
56861     }
56862
56863     
56864     
56865 };
56866 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56867     /**
56868      * @cfg {Store} eventStore The store that loads events.
56869      */
56870     eventStore : 25,
56871
56872      
56873     activeDate : false,
56874     startDay : 0,
56875     autoWidth : true,
56876     monitorWindowResize : false,
56877
56878     
56879     resizeColumns : function() {
56880         var col = (this.view.el.getWidth() / 7) - 3;
56881         // loop through cols, and setWidth
56882         for(var i =0 ; i < 7 ; i++){
56883             this.cm.setColumnWidth(i, col);
56884         }
56885     },
56886      setDate :function(date) {
56887         
56888         Roo.log('setDate?');
56889         
56890         this.resizeColumns();
56891         var vd = this.activeDate;
56892         this.activeDate = date;
56893 //        if(vd && this.el){
56894 //            var t = date.getTime();
56895 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56896 //                Roo.log('using add remove');
56897 //                
56898 //                this.fireEvent('monthchange', this, date);
56899 //                
56900 //                this.cells.removeClass("fc-state-highlight");
56901 //                this.cells.each(function(c){
56902 //                   if(c.dateValue == t){
56903 //                       c.addClass("fc-state-highlight");
56904 //                       setTimeout(function(){
56905 //                            try{c.dom.firstChild.focus();}catch(e){}
56906 //                       }, 50);
56907 //                       return false;
56908 //                   }
56909 //                   return true;
56910 //                });
56911 //                return;
56912 //            }
56913 //        }
56914         
56915         var days = date.getDaysInMonth();
56916         
56917         var firstOfMonth = date.getFirstDateOfMonth();
56918         var startingPos = firstOfMonth.getDay()-this.startDay;
56919         
56920         if(startingPos < this.startDay){
56921             startingPos += 7;
56922         }
56923         
56924         var pm = date.add(Date.MONTH, -1);
56925         var prevStart = pm.getDaysInMonth()-startingPos;
56926 //        
56927         
56928         
56929         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56930         
56931         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
56932         //this.cells.addClassOnOver('fc-state-hover');
56933         
56934         var cells = this.cells.elements;
56935         var textEls = this.textNodes;
56936         
56937         //Roo.each(cells, function(cell){
56938         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
56939         //});
56940         
56941         days += startingPos;
56942
56943         // convert everything to numbers so it's fast
56944         var day = 86400000;
56945         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
56946         //Roo.log(d);
56947         //Roo.log(pm);
56948         //Roo.log(prevStart);
56949         
56950         var today = new Date().clearTime().getTime();
56951         var sel = date.clearTime().getTime();
56952         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
56953         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
56954         var ddMatch = this.disabledDatesRE;
56955         var ddText = this.disabledDatesText;
56956         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
56957         var ddaysText = this.disabledDaysText;
56958         var format = this.format;
56959         
56960         var setCellClass = function(cal, cell){
56961             
56962             //Roo.log('set Cell Class');
56963             cell.title = "";
56964             var t = d.getTime();
56965             
56966             //Roo.log(d);
56967             
56968             
56969             cell.dateValue = t;
56970             if(t == today){
56971                 cell.className += " fc-today";
56972                 cell.className += " fc-state-highlight";
56973                 cell.title = cal.todayText;
56974             }
56975             if(t == sel){
56976                 // disable highlight in other month..
56977                 cell.className += " fc-state-highlight";
56978                 
56979             }
56980             // disabling
56981             if(t < min) {
56982                 //cell.className = " fc-state-disabled";
56983                 cell.title = cal.minText;
56984                 return;
56985             }
56986             if(t > max) {
56987                 //cell.className = " fc-state-disabled";
56988                 cell.title = cal.maxText;
56989                 return;
56990             }
56991             if(ddays){
56992                 if(ddays.indexOf(d.getDay()) != -1){
56993                     // cell.title = ddaysText;
56994                    // cell.className = " fc-state-disabled";
56995                 }
56996             }
56997             if(ddMatch && format){
56998                 var fvalue = d.dateFormat(format);
56999                 if(ddMatch.test(fvalue)){
57000                     cell.title = ddText.replace("%0", fvalue);
57001                    cell.className = " fc-state-disabled";
57002                 }
57003             }
57004             
57005             if (!cell.initialClassName) {
57006                 cell.initialClassName = cell.dom.className;
57007             }
57008             
57009             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57010         };
57011
57012         var i = 0;
57013         
57014         for(; i < startingPos; i++) {
57015             cells[i].dayName =  (++prevStart);
57016             Roo.log(textEls[i]);
57017             d.setDate(d.getDate()+1);
57018             
57019             //cells[i].className = "fc-past fc-other-month";
57020             setCellClass(this, cells[i]);
57021         }
57022         
57023         var intDay = 0;
57024         
57025         for(; i < days; i++){
57026             intDay = i - startingPos + 1;
57027             cells[i].dayName =  (intDay);
57028             d.setDate(d.getDate()+1);
57029             
57030             cells[i].className = ''; // "x-date-active";
57031             setCellClass(this, cells[i]);
57032         }
57033         var extraDays = 0;
57034         
57035         for(; i < 42; i++) {
57036             //textEls[i].innerHTML = (++extraDays);
57037             
57038             d.setDate(d.getDate()+1);
57039             cells[i].dayName = (++extraDays);
57040             cells[i].className = "fc-future fc-other-month";
57041             setCellClass(this, cells[i]);
57042         }
57043         
57044         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57045         
57046         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57047         
57048         // this will cause all the cells to mis
57049         var rows= [];
57050         var i =0;
57051         for (var r = 0;r < 6;r++) {
57052             for (var c =0;c < 7;c++) {
57053                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57054             }    
57055         }
57056         
57057         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57058         for(i=0;i<cells.length;i++) {
57059             
57060             this.cells.elements[i].dayName = cells[i].dayName ;
57061             this.cells.elements[i].className = cells[i].className;
57062             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57063             this.cells.elements[i].title = cells[i].title ;
57064             this.cells.elements[i].dateValue = cells[i].dateValue ;
57065         }
57066         
57067         
57068         
57069         
57070         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57071         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57072         
57073         ////if(totalRows != 6){
57074             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57075            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57076        // }
57077         
57078         this.fireEvent('monthchange', this, date);
57079         
57080         
57081     },
57082  /**
57083      * Returns the grid's SelectionModel.
57084      * @return {SelectionModel}
57085      */
57086     getSelectionModel : function(){
57087         if(!this.selModel){
57088             this.selModel = new Roo.grid.CellSelectionModel();
57089         }
57090         return this.selModel;
57091     },
57092
57093     load: function() {
57094         this.eventStore.load()
57095         
57096         
57097         
57098     },
57099     
57100     findCell : function(dt) {
57101         dt = dt.clearTime().getTime();
57102         var ret = false;
57103         this.cells.each(function(c){
57104             //Roo.log("check " +c.dateValue + '?=' + dt);
57105             if(c.dateValue == dt){
57106                 ret = c;
57107                 return false;
57108             }
57109             return true;
57110         });
57111         
57112         return ret;
57113     },
57114     
57115     findCells : function(rec) {
57116         var s = rec.data.start_dt.clone().clearTime().getTime();
57117        // Roo.log(s);
57118         var e= rec.data.end_dt.clone().clearTime().getTime();
57119        // Roo.log(e);
57120         var ret = [];
57121         this.cells.each(function(c){
57122              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57123             
57124             if(c.dateValue > e){
57125                 return ;
57126             }
57127             if(c.dateValue < s){
57128                 return ;
57129             }
57130             ret.push(c);
57131         });
57132         
57133         return ret;    
57134     },
57135     
57136     findBestRow: function(cells)
57137     {
57138         var ret = 0;
57139         
57140         for (var i =0 ; i < cells.length;i++) {
57141             ret  = Math.max(cells[i].rows || 0,ret);
57142         }
57143         return ret;
57144         
57145     },
57146     
57147     
57148     addItem : function(rec)
57149     {
57150         // look for vertical location slot in
57151         var cells = this.findCells(rec);
57152         
57153         rec.row = this.findBestRow(cells);
57154         
57155         // work out the location.
57156         
57157         var crow = false;
57158         var rows = [];
57159         for(var i =0; i < cells.length; i++) {
57160             if (!crow) {
57161                 crow = {
57162                     start : cells[i],
57163                     end :  cells[i]
57164                 };
57165                 continue;
57166             }
57167             if (crow.start.getY() == cells[i].getY()) {
57168                 // on same row.
57169                 crow.end = cells[i];
57170                 continue;
57171             }
57172             // different row.
57173             rows.push(crow);
57174             crow = {
57175                 start: cells[i],
57176                 end : cells[i]
57177             };
57178             
57179         }
57180         
57181         rows.push(crow);
57182         rec.els = [];
57183         rec.rows = rows;
57184         rec.cells = cells;
57185         for (var i = 0; i < cells.length;i++) {
57186             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57187             
57188         }
57189         
57190         
57191     },
57192     
57193     clearEvents: function() {
57194         
57195         if (!this.eventStore.getCount()) {
57196             return;
57197         }
57198         // reset number of rows in cells.
57199         Roo.each(this.cells.elements, function(c){
57200             c.rows = 0;
57201         });
57202         
57203         this.eventStore.each(function(e) {
57204             this.clearEvent(e);
57205         },this);
57206         
57207     },
57208     
57209     clearEvent : function(ev)
57210     {
57211         if (ev.els) {
57212             Roo.each(ev.els, function(el) {
57213                 el.un('mouseenter' ,this.onEventEnter, this);
57214                 el.un('mouseleave' ,this.onEventLeave, this);
57215                 el.remove();
57216             },this);
57217             ev.els = [];
57218         }
57219     },
57220     
57221     
57222     renderEvent : function(ev,ctr) {
57223         if (!ctr) {
57224              ctr = this.view.el.select('.fc-event-container',true).first();
57225         }
57226         
57227          
57228         this.clearEvent(ev);
57229             //code
57230        
57231         
57232         
57233         ev.els = [];
57234         var cells = ev.cells;
57235         var rows = ev.rows;
57236         this.fireEvent('eventrender', this, ev);
57237         
57238         for(var i =0; i < rows.length; i++) {
57239             
57240             cls = '';
57241             if (i == 0) {
57242                 cls += ' fc-event-start';
57243             }
57244             if ((i+1) == rows.length) {
57245                 cls += ' fc-event-end';
57246             }
57247             
57248             //Roo.log(ev.data);
57249             // how many rows should it span..
57250             var cg = this.eventTmpl.append(ctr,Roo.apply({
57251                 fccls : cls
57252                 
57253             }, ev.data) , true);
57254             
57255             
57256             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57257             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57258             cg.on('click', this.onEventClick, this, ev);
57259             
57260             ev.els.push(cg);
57261             
57262             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57263             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57264             //Roo.log(cg);
57265              
57266             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57267             cg.setWidth(ebox.right - sbox.x -2);
57268         }
57269     },
57270     
57271     renderEvents: function()
57272     {   
57273         // first make sure there is enough space..
57274         
57275         if (!this.eventTmpl) {
57276             this.eventTmpl = new Roo.Template(
57277                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57278                     '<div class="fc-event-inner">' +
57279                         '<span class="fc-event-time">{time}</span>' +
57280                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57281                     '</div>' +
57282                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57283                 '</div>'
57284             );
57285                 
57286         }
57287                
57288         
57289         
57290         this.cells.each(function(c) {
57291             //Roo.log(c.select('.fc-day-content div',true).first());
57292             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57293         });
57294         
57295         var ctr = this.view.el.select('.fc-event-container',true).first();
57296         
57297         var cls;
57298         this.eventStore.each(function(ev){
57299             
57300             this.renderEvent(ev);
57301              
57302              
57303         }, this);
57304         this.view.layout();
57305         
57306     },
57307     
57308     onEventEnter: function (e, el,event,d) {
57309         this.fireEvent('evententer', this, el, event);
57310     },
57311     
57312     onEventLeave: function (e, el,event,d) {
57313         this.fireEvent('eventleave', this, el, event);
57314     },
57315     
57316     onEventClick: function (e, el,event,d) {
57317         this.fireEvent('eventclick', this, el, event);
57318     },
57319     
57320     onMonthChange: function () {
57321         this.store.load();
57322     },
57323     
57324     onLoad: function () {
57325         
57326         //Roo.log('calendar onload');
57327 //         
57328         if(this.eventStore.getCount() > 0){
57329             
57330            
57331             
57332             this.eventStore.each(function(d){
57333                 
57334                 
57335                 // FIXME..
57336                 var add =   d.data;
57337                 if (typeof(add.end_dt) == 'undefined')  {
57338                     Roo.log("Missing End time in calendar data: ");
57339                     Roo.log(d);
57340                     return;
57341                 }
57342                 if (typeof(add.start_dt) == 'undefined')  {
57343                     Roo.log("Missing Start time in calendar data: ");
57344                     Roo.log(d);
57345                     return;
57346                 }
57347                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57348                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57349                 add.id = add.id || d.id;
57350                 add.title = add.title || '??';
57351                 
57352                 this.addItem(d);
57353                 
57354              
57355             },this);
57356         }
57357         
57358         this.renderEvents();
57359     }
57360     
57361
57362 });
57363 /*
57364  grid : {
57365                 xtype: 'Grid',
57366                 xns: Roo.grid,
57367                 listeners : {
57368                     render : function ()
57369                     {
57370                         _this.grid = this;
57371                         
57372                         if (!this.view.el.hasClass('course-timesheet')) {
57373                             this.view.el.addClass('course-timesheet');
57374                         }
57375                         if (this.tsStyle) {
57376                             this.ds.load({});
57377                             return; 
57378                         }
57379                         Roo.log('width');
57380                         Roo.log(_this.grid.view.el.getWidth());
57381                         
57382                         
57383                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57384                             '.course-timesheet .x-grid-row' : {
57385                                 height: '80px'
57386                             },
57387                             '.x-grid-row td' : {
57388                                 'vertical-align' : 0
57389                             },
57390                             '.course-edit-link' : {
57391                                 'color' : 'blue',
57392                                 'text-overflow' : 'ellipsis',
57393                                 'overflow' : 'hidden',
57394                                 'white-space' : 'nowrap',
57395                                 'cursor' : 'pointer'
57396                             },
57397                             '.sub-link' : {
57398                                 'color' : 'green'
57399                             },
57400                             '.de-act-sup-link' : {
57401                                 'color' : 'purple',
57402                                 'text-decoration' : 'line-through'
57403                             },
57404                             '.de-act-link' : {
57405                                 'color' : 'red',
57406                                 'text-decoration' : 'line-through'
57407                             },
57408                             '.course-timesheet .course-highlight' : {
57409                                 'border-top-style': 'dashed !important',
57410                                 'border-bottom-bottom': 'dashed !important'
57411                             },
57412                             '.course-timesheet .course-item' : {
57413                                 'font-family'   : 'tahoma, arial, helvetica',
57414                                 'font-size'     : '11px',
57415                                 'overflow'      : 'hidden',
57416                                 'padding-left'  : '10px',
57417                                 'padding-right' : '10px',
57418                                 'padding-top' : '10px' 
57419                             }
57420                             
57421                         }, Roo.id());
57422                                 this.ds.load({});
57423                     }
57424                 },
57425                 autoWidth : true,
57426                 monitorWindowResize : false,
57427                 cellrenderer : function(v,x,r)
57428                 {
57429                     return v;
57430                 },
57431                 sm : {
57432                     xtype: 'CellSelectionModel',
57433                     xns: Roo.grid
57434                 },
57435                 dataSource : {
57436                     xtype: 'Store',
57437                     xns: Roo.data,
57438                     listeners : {
57439                         beforeload : function (_self, options)
57440                         {
57441                             options.params = options.params || {};
57442                             options.params._month = _this.monthField.getValue();
57443                             options.params.limit = 9999;
57444                             options.params['sort'] = 'when_dt';    
57445                             options.params['dir'] = 'ASC';    
57446                             this.proxy.loadResponse = this.loadResponse;
57447                             Roo.log("load?");
57448                             //this.addColumns();
57449                         },
57450                         load : function (_self, records, options)
57451                         {
57452                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57453                                 // if you click on the translation.. you can edit it...
57454                                 var el = Roo.get(this);
57455                                 var id = el.dom.getAttribute('data-id');
57456                                 var d = el.dom.getAttribute('data-date');
57457                                 var t = el.dom.getAttribute('data-time');
57458                                 //var id = this.child('span').dom.textContent;
57459                                 
57460                                 //Roo.log(this);
57461                                 Pman.Dialog.CourseCalendar.show({
57462                                     id : id,
57463                                     when_d : d,
57464                                     when_t : t,
57465                                     productitem_active : id ? 1 : 0
57466                                 }, function() {
57467                                     _this.grid.ds.load({});
57468                                 });
57469                            
57470                            });
57471                            
57472                            _this.panel.fireEvent('resize', [ '', '' ]);
57473                         }
57474                     },
57475                     loadResponse : function(o, success, response){
57476                             // this is overridden on before load..
57477                             
57478                             Roo.log("our code?");       
57479                             //Roo.log(success);
57480                             //Roo.log(response)
57481                             delete this.activeRequest;
57482                             if(!success){
57483                                 this.fireEvent("loadexception", this, o, response);
57484                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57485                                 return;
57486                             }
57487                             var result;
57488                             try {
57489                                 result = o.reader.read(response);
57490                             }catch(e){
57491                                 Roo.log("load exception?");
57492                                 this.fireEvent("loadexception", this, o, response, e);
57493                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57494                                 return;
57495                             }
57496                             Roo.log("ready...");        
57497                             // loop through result.records;
57498                             // and set this.tdate[date] = [] << array of records..
57499                             _this.tdata  = {};
57500                             Roo.each(result.records, function(r){
57501                                 //Roo.log(r.data);
57502                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57503                                     _this.tdata[r.data.when_dt.format('j')] = [];
57504                                 }
57505                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57506                             });
57507                             
57508                             //Roo.log(_this.tdata);
57509                             
57510                             result.records = [];
57511                             result.totalRecords = 6;
57512                     
57513                             // let's generate some duumy records for the rows.
57514                             //var st = _this.dateField.getValue();
57515                             
57516                             // work out monday..
57517                             //st = st.add(Date.DAY, -1 * st.format('w'));
57518                             
57519                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57520                             
57521                             var firstOfMonth = date.getFirstDayOfMonth();
57522                             var days = date.getDaysInMonth();
57523                             var d = 1;
57524                             var firstAdded = false;
57525                             for (var i = 0; i < result.totalRecords ; i++) {
57526                                 //var d= st.add(Date.DAY, i);
57527                                 var row = {};
57528                                 var added = 0;
57529                                 for(var w = 0 ; w < 7 ; w++){
57530                                     if(!firstAdded && firstOfMonth != w){
57531                                         continue;
57532                                     }
57533                                     if(d > days){
57534                                         continue;
57535                                     }
57536                                     firstAdded = true;
57537                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57538                                     row['weekday'+w] = String.format(
57539                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57540                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57541                                                     d,
57542                                                     date.format('Y-m-')+dd
57543                                                 );
57544                                     added++;
57545                                     if(typeof(_this.tdata[d]) != 'undefined'){
57546                                         Roo.each(_this.tdata[d], function(r){
57547                                             var is_sub = '';
57548                                             var deactive = '';
57549                                             var id = r.id;
57550                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57551                                             if(r.parent_id*1>0){
57552                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57553                                                 id = r.parent_id;
57554                                             }
57555                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57556                                                 deactive = 'de-act-link';
57557                                             }
57558                                             
57559                                             row['weekday'+w] += String.format(
57560                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57561                                                     id, //0
57562                                                     r.product_id_name, //1
57563                                                     r.when_dt.format('h:ia'), //2
57564                                                     is_sub, //3
57565                                                     deactive, //4
57566                                                     desc // 5
57567                                             );
57568                                         });
57569                                     }
57570                                     d++;
57571                                 }
57572                                 
57573                                 // only do this if something added..
57574                                 if(added > 0){ 
57575                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57576                                 }
57577                                 
57578                                 
57579                                 // push it twice. (second one with an hour..
57580                                 
57581                             }
57582                             //Roo.log(result);
57583                             this.fireEvent("load", this, o, o.request.arg);
57584                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57585                         },
57586                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57587                     proxy : {
57588                         xtype: 'HttpProxy',
57589                         xns: Roo.data,
57590                         method : 'GET',
57591                         url : baseURL + '/Roo/Shop_course.php'
57592                     },
57593                     reader : {
57594                         xtype: 'JsonReader',
57595                         xns: Roo.data,
57596                         id : 'id',
57597                         fields : [
57598                             {
57599                                 'name': 'id',
57600                                 'type': 'int'
57601                             },
57602                             {
57603                                 'name': 'when_dt',
57604                                 'type': 'string'
57605                             },
57606                             {
57607                                 'name': 'end_dt',
57608                                 'type': 'string'
57609                             },
57610                             {
57611                                 'name': 'parent_id',
57612                                 'type': 'int'
57613                             },
57614                             {
57615                                 'name': 'product_id',
57616                                 'type': 'int'
57617                             },
57618                             {
57619                                 'name': 'productitem_id',
57620                                 'type': 'int'
57621                             },
57622                             {
57623                                 'name': 'guid',
57624                                 'type': 'int'
57625                             }
57626                         ]
57627                     }
57628                 },
57629                 toolbar : {
57630                     xtype: 'Toolbar',
57631                     xns: Roo,
57632                     items : [
57633                         {
57634                             xtype: 'Button',
57635                             xns: Roo.Toolbar,
57636                             listeners : {
57637                                 click : function (_self, e)
57638                                 {
57639                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57640                                     sd.setMonth(sd.getMonth()-1);
57641                                     _this.monthField.setValue(sd.format('Y-m-d'));
57642                                     _this.grid.ds.load({});
57643                                 }
57644                             },
57645                             text : "Back"
57646                         },
57647                         {
57648                             xtype: 'Separator',
57649                             xns: Roo.Toolbar
57650                         },
57651                         {
57652                             xtype: 'MonthField',
57653                             xns: Roo.form,
57654                             listeners : {
57655                                 render : function (_self)
57656                                 {
57657                                     _this.monthField = _self;
57658                                    // _this.monthField.set  today
57659                                 },
57660                                 select : function (combo, date)
57661                                 {
57662                                     _this.grid.ds.load({});
57663                                 }
57664                             },
57665                             value : (function() { return new Date(); })()
57666                         },
57667                         {
57668                             xtype: 'Separator',
57669                             xns: Roo.Toolbar
57670                         },
57671                         {
57672                             xtype: 'TextItem',
57673                             xns: Roo.Toolbar,
57674                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57675                         },
57676                         {
57677                             xtype: 'Fill',
57678                             xns: Roo.Toolbar
57679                         },
57680                         {
57681                             xtype: 'Button',
57682                             xns: Roo.Toolbar,
57683                             listeners : {
57684                                 click : function (_self, e)
57685                                 {
57686                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57687                                     sd.setMonth(sd.getMonth()+1);
57688                                     _this.monthField.setValue(sd.format('Y-m-d'));
57689                                     _this.grid.ds.load({});
57690                                 }
57691                             },
57692                             text : "Next"
57693                         }
57694                     ]
57695                 },
57696                  
57697             }
57698         };
57699         
57700         *//*
57701  * Based on:
57702  * Ext JS Library 1.1.1
57703  * Copyright(c) 2006-2007, Ext JS, LLC.
57704  *
57705  * Originally Released Under LGPL - original licence link has changed is not relivant.
57706  *
57707  * Fork - LGPL
57708  * <script type="text/javascript">
57709  */
57710  
57711 /**
57712  * @class Roo.LoadMask
57713  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57714  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57715  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57716  * element's UpdateManager load indicator and will be destroyed after the initial load.
57717  * @constructor
57718  * Create a new LoadMask
57719  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57720  * @param {Object} config The config object
57721  */
57722 Roo.LoadMask = function(el, config){
57723     this.el = Roo.get(el);
57724     Roo.apply(this, config);
57725     if(this.store){
57726         this.store.on('beforeload', this.onBeforeLoad, this);
57727         this.store.on('load', this.onLoad, this);
57728         this.store.on('loadexception', this.onLoadException, this);
57729         this.removeMask = false;
57730     }else{
57731         var um = this.el.getUpdateManager();
57732         um.showLoadIndicator = false; // disable the default indicator
57733         um.on('beforeupdate', this.onBeforeLoad, this);
57734         um.on('update', this.onLoad, this);
57735         um.on('failure', this.onLoad, this);
57736         this.removeMask = true;
57737     }
57738 };
57739
57740 Roo.LoadMask.prototype = {
57741     /**
57742      * @cfg {Boolean} removeMask
57743      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57744      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57745      */
57746     /**
57747      * @cfg {String} msg
57748      * The text to display in a centered loading message box (defaults to 'Loading...')
57749      */
57750     msg : 'Loading...',
57751     /**
57752      * @cfg {String} msgCls
57753      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57754      */
57755     msgCls : 'x-mask-loading',
57756
57757     /**
57758      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57759      * @type Boolean
57760      */
57761     disabled: false,
57762
57763     /**
57764      * Disables the mask to prevent it from being displayed
57765      */
57766     disable : function(){
57767        this.disabled = true;
57768     },
57769
57770     /**
57771      * Enables the mask so that it can be displayed
57772      */
57773     enable : function(){
57774         this.disabled = false;
57775     },
57776     
57777     onLoadException : function()
57778     {
57779         Roo.log(arguments);
57780         
57781         if (typeof(arguments[3]) != 'undefined') {
57782             Roo.MessageBox.alert("Error loading",arguments[3]);
57783         } 
57784         /*
57785         try {
57786             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57787                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57788             }   
57789         } catch(e) {
57790             
57791         }
57792         */
57793     
57794         
57795         
57796         this.el.unmask(this.removeMask);
57797     },
57798     // private
57799     onLoad : function()
57800     {
57801         this.el.unmask(this.removeMask);
57802     },
57803
57804     // private
57805     onBeforeLoad : function(){
57806         if(!this.disabled){
57807             this.el.mask(this.msg, this.msgCls);
57808         }
57809     },
57810
57811     // private
57812     destroy : function(){
57813         if(this.store){
57814             this.store.un('beforeload', this.onBeforeLoad, this);
57815             this.store.un('load', this.onLoad, this);
57816             this.store.un('loadexception', this.onLoadException, this);
57817         }else{
57818             var um = this.el.getUpdateManager();
57819             um.un('beforeupdate', this.onBeforeLoad, this);
57820             um.un('update', this.onLoad, this);
57821             um.un('failure', this.onLoad, this);
57822         }
57823     }
57824 };/*
57825  * Based on:
57826  * Ext JS Library 1.1.1
57827  * Copyright(c) 2006-2007, Ext JS, LLC.
57828  *
57829  * Originally Released Under LGPL - original licence link has changed is not relivant.
57830  *
57831  * Fork - LGPL
57832  * <script type="text/javascript">
57833  */
57834
57835
57836 /**
57837  * @class Roo.XTemplate
57838  * @extends Roo.Template
57839  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57840 <pre><code>
57841 var t = new Roo.XTemplate(
57842         '&lt;select name="{name}"&gt;',
57843                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57844         '&lt;/select&gt;'
57845 );
57846  
57847 // then append, applying the master template values
57848  </code></pre>
57849  *
57850  * Supported features:
57851  *
57852  *  Tags:
57853
57854 <pre><code>
57855       {a_variable} - output encoded.
57856       {a_variable.format:("Y-m-d")} - call a method on the variable
57857       {a_variable:raw} - unencoded output
57858       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57859       {a_variable:this.method_on_template(...)} - call a method on the template object.
57860  
57861 </code></pre>
57862  *  The tpl tag:
57863 <pre><code>
57864         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57865         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57866         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57867         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57868   
57869         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57870         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57871 </code></pre>
57872  *      
57873  */
57874 Roo.XTemplate = function()
57875 {
57876     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57877     if (this.html) {
57878         this.compile();
57879     }
57880 };
57881
57882
57883 Roo.extend(Roo.XTemplate, Roo.Template, {
57884
57885     /**
57886      * The various sub templates
57887      */
57888     tpls : false,
57889     /**
57890      *
57891      * basic tag replacing syntax
57892      * WORD:WORD()
57893      *
57894      * // you can fake an object call by doing this
57895      *  x.t:(test,tesT) 
57896      * 
57897      */
57898     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57899
57900     /**
57901      * compile the template
57902      *
57903      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57904      *
57905      */
57906     compile: function()
57907     {
57908         var s = this.html;
57909      
57910         s = ['<tpl>', s, '</tpl>'].join('');
57911     
57912         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57913             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57914             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57915             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57916             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57917             m,
57918             id     = 0,
57919             tpls   = [];
57920     
57921         while(true == !!(m = s.match(re))){
57922             var forMatch   = m[0].match(nameRe),
57923                 ifMatch   = m[0].match(ifRe),
57924                 execMatch   = m[0].match(execRe),
57925                 namedMatch   = m[0].match(namedRe),
57926                 
57927                 exp  = null, 
57928                 fn   = null,
57929                 exec = null,
57930                 name = forMatch && forMatch[1] ? forMatch[1] : '';
57931                 
57932             if (ifMatch) {
57933                 // if - puts fn into test..
57934                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
57935                 if(exp){
57936                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
57937                 }
57938             }
57939             
57940             if (execMatch) {
57941                 // exec - calls a function... returns empty if true is  returned.
57942                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
57943                 if(exp){
57944                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
57945                 }
57946             }
57947             
57948             
57949             if (name) {
57950                 // for = 
57951                 switch(name){
57952                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
57953                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
57954                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
57955                 }
57956             }
57957             var uid = namedMatch ? namedMatch[1] : id;
57958             
57959             
57960             tpls.push({
57961                 id:     namedMatch ? namedMatch[1] : id,
57962                 target: name,
57963                 exec:   exec,
57964                 test:   fn,
57965                 body:   m[1] || ''
57966             });
57967             if (namedMatch) {
57968                 s = s.replace(m[0], '');
57969             } else { 
57970                 s = s.replace(m[0], '{xtpl'+ id + '}');
57971             }
57972             ++id;
57973         }
57974         this.tpls = [];
57975         for(var i = tpls.length-1; i >= 0; --i){
57976             this.compileTpl(tpls[i]);
57977             this.tpls[tpls[i].id] = tpls[i];
57978         }
57979         this.master = tpls[tpls.length-1];
57980         return this;
57981     },
57982     /**
57983      * same as applyTemplate, except it's done to one of the subTemplates
57984      * when using named templates, you can do:
57985      *
57986      * var str = pl.applySubTemplate('your-name', values);
57987      *
57988      * 
57989      * @param {Number} id of the template
57990      * @param {Object} values to apply to template
57991      * @param {Object} parent (normaly the instance of this object)
57992      */
57993     applySubTemplate : function(id, values, parent)
57994     {
57995         
57996         
57997         var t = this.tpls[id];
57998         
57999         
58000         try { 
58001             if(t.test && !t.test.call(this, values, parent)){
58002                 return '';
58003             }
58004         } catch(e) {
58005             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58006             Roo.log(e.toString());
58007             Roo.log(t.test);
58008             return ''
58009         }
58010         try { 
58011             
58012             if(t.exec && t.exec.call(this, values, parent)){
58013                 return '';
58014             }
58015         } catch(e) {
58016             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58017             Roo.log(e.toString());
58018             Roo.log(t.exec);
58019             return ''
58020         }
58021         try {
58022             var vs = t.target ? t.target.call(this, values, parent) : values;
58023             parent = t.target ? values : parent;
58024             if(t.target && vs instanceof Array){
58025                 var buf = [];
58026                 for(var i = 0, len = vs.length; i < len; i++){
58027                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58028                 }
58029                 return buf.join('');
58030             }
58031             return t.compiled.call(this, vs, parent);
58032         } catch (e) {
58033             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58034             Roo.log(e.toString());
58035             Roo.log(t.compiled);
58036             return '';
58037         }
58038     },
58039
58040     compileTpl : function(tpl)
58041     {
58042         var fm = Roo.util.Format;
58043         var useF = this.disableFormats !== true;
58044         var sep = Roo.isGecko ? "+" : ",";
58045         var undef = function(str) {
58046             Roo.log("Property not found :"  + str);
58047             return '';
58048         };
58049         
58050         var fn = function(m, name, format, args)
58051         {
58052             //Roo.log(arguments);
58053             args = args ? args.replace(/\\'/g,"'") : args;
58054             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58055             if (typeof(format) == 'undefined') {
58056                 format= 'htmlEncode';
58057             }
58058             if (format == 'raw' ) {
58059                 format = false;
58060             }
58061             
58062             if(name.substr(0, 4) == 'xtpl'){
58063                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58064             }
58065             
58066             // build an array of options to determine if value is undefined..
58067             
58068             // basically get 'xxxx.yyyy' then do
58069             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58070             //    (function () { Roo.log("Property not found"); return ''; })() :
58071             //    ......
58072             
58073             var udef_ar = [];
58074             var lookfor = '';
58075             Roo.each(name.split('.'), function(st) {
58076                 lookfor += (lookfor.length ? '.': '') + st;
58077                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58078             });
58079             
58080             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58081             
58082             
58083             if(format && useF){
58084                 
58085                 args = args ? ',' + args : "";
58086                  
58087                 if(format.substr(0, 5) != "this."){
58088                     format = "fm." + format + '(';
58089                 }else{
58090                     format = 'this.call("'+ format.substr(5) + '", ';
58091                     args = ", values";
58092                 }
58093                 
58094                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58095             }
58096              
58097             if (args.length) {
58098                 // called with xxyx.yuu:(test,test)
58099                 // change to ()
58100                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58101             }
58102             // raw.. - :raw modifier..
58103             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58104             
58105         };
58106         var body;
58107         // branched to use + in gecko and [].join() in others
58108         if(Roo.isGecko){
58109             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58110                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58111                     "';};};";
58112         }else{
58113             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58114             body.push(tpl.body.replace(/(\r\n|\n)/g,
58115                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58116             body.push("'].join('');};};");
58117             body = body.join('');
58118         }
58119         
58120         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58121        
58122         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58123         eval(body);
58124         
58125         return this;
58126     },
58127
58128     applyTemplate : function(values){
58129         return this.master.compiled.call(this, values, {});
58130         //var s = this.subs;
58131     },
58132
58133     apply : function(){
58134         return this.applyTemplate.apply(this, arguments);
58135     }
58136
58137  });
58138
58139 Roo.XTemplate.from = function(el){
58140     el = Roo.getDom(el);
58141     return new Roo.XTemplate(el.value || el.innerHTML);
58142 };