roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4848 <p>
4849 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4850
4851 <p>
4852 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String} name The attribute name
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (!this.dom.hasAttribute(name)) {
9582                 return undefined;
9583             }
9584             return this.dom.getAttribute(name);
9585         }
9586         
9587         
9588         
9589     };
9590
9591     var ep = El.prototype;
9592
9593     /**
9594      * Appends an event handler (Shorthand for addListener)
9595      * @param {String}   eventName     The type of event to append
9596      * @param {Function} fn        The method the event invokes
9597      * @param {Object} scope       (optional) The scope (this object) of the fn
9598      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9599      * @method
9600      */
9601     ep.on = ep.addListener;
9602         // backwards compat
9603     ep.mon = ep.addListener;
9604
9605     /**
9606      * Removes an event handler from this element (shorthand for removeListener)
9607      * @param {String} eventName the type of event to remove
9608      * @param {Function} fn the method the event invokes
9609      * @return {Roo.Element} this
9610      * @method
9611      */
9612     ep.un = ep.removeListener;
9613
9614     /**
9615      * true to automatically adjust width and height settings for box-model issues (default to true)
9616      */
9617     ep.autoBoxAdjust = true;
9618
9619     // private
9620     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9621
9622     // private
9623     El.addUnits = function(v, defaultUnit){
9624         if(v === "" || v == "auto"){
9625             return v;
9626         }
9627         if(v === undefined){
9628             return '';
9629         }
9630         if(typeof v == "number" || !El.unitPattern.test(v)){
9631             return v + (defaultUnit || 'px');
9632         }
9633         return v;
9634     };
9635
9636     // special markup used throughout Roo when box wrapping elements
9637     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9638     /**
9639      * Visibility mode constant - Use visibility to hide element
9640      * @static
9641      * @type Number
9642      */
9643     El.VISIBILITY = 1;
9644     /**
9645      * Visibility mode constant - Use display to hide element
9646      * @static
9647      * @type Number
9648      */
9649     El.DISPLAY = 2;
9650
9651     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9652     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9653     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9654
9655
9656
9657     /**
9658      * @private
9659      */
9660     El.cache = {};
9661
9662     var docEl;
9663
9664     /**
9665      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9666      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9667      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9668      * @return {Element} The Element object
9669      * @static
9670      */
9671     El.get = function(el){
9672         var ex, elm, id;
9673         if(!el){ return null; }
9674         if(typeof el == "string"){ // element id
9675             if(!(elm = document.getElementById(el))){
9676                 return null;
9677             }
9678             if(ex = El.cache[el]){
9679                 ex.dom = elm;
9680             }else{
9681                 ex = El.cache[el] = new El(elm);
9682             }
9683             return ex;
9684         }else if(el.tagName){ // dom element
9685             if(!(id = el.id)){
9686                 id = Roo.id(el);
9687             }
9688             if(ex = El.cache[id]){
9689                 ex.dom = el;
9690             }else{
9691                 ex = El.cache[id] = new El(el);
9692             }
9693             return ex;
9694         }else if(el instanceof El){
9695             if(el != docEl){
9696                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9697                                                               // catch case where it hasn't been appended
9698                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9699             }
9700             return el;
9701         }else if(el.isComposite){
9702             return el;
9703         }else if(el instanceof Array){
9704             return El.select(el);
9705         }else if(el == document){
9706             // create a bogus element object representing the document object
9707             if(!docEl){
9708                 var f = function(){};
9709                 f.prototype = El.prototype;
9710                 docEl = new f();
9711                 docEl.dom = document;
9712             }
9713             return docEl;
9714         }
9715         return null;
9716     };
9717
9718     // private
9719     El.uncache = function(el){
9720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9721             if(a[i]){
9722                 delete El.cache[a[i].id || a[i]];
9723             }
9724         }
9725     };
9726
9727     // private
9728     // Garbage collection - uncache elements/purge listeners on orphaned elements
9729     // so we don't hold a reference and cause the browser to retain them
9730     El.garbageCollect = function(){
9731         if(!Roo.enableGarbageCollector){
9732             clearInterval(El.collectorThread);
9733             return;
9734         }
9735         for(var eid in El.cache){
9736             var el = El.cache[eid], d = el.dom;
9737             // -------------------------------------------------------
9738             // Determining what is garbage:
9739             // -------------------------------------------------------
9740             // !d
9741             // dom node is null, definitely garbage
9742             // -------------------------------------------------------
9743             // !d.parentNode
9744             // no parentNode == direct orphan, definitely garbage
9745             // -------------------------------------------------------
9746             // !d.offsetParent && !document.getElementById(eid)
9747             // display none elements have no offsetParent so we will
9748             // also try to look it up by it's id. However, check
9749             // offsetParent first so we don't do unneeded lookups.
9750             // This enables collection of elements that are not orphans
9751             // directly, but somewhere up the line they have an orphan
9752             // parent.
9753             // -------------------------------------------------------
9754             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9755                 delete El.cache[eid];
9756                 if(d && Roo.enableListenerCollection){
9757                     E.purgeElement(d);
9758                 }
9759             }
9760         }
9761     }
9762     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9763
9764
9765     // dom is optional
9766     El.Flyweight = function(dom){
9767         this.dom = dom;
9768     };
9769     El.Flyweight.prototype = El.prototype;
9770
9771     El._flyweights = {};
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * @param {String/HTMLElement} el The dom node or id
9776      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9777      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9778      * @static
9779      * @return {Element} The shared Element object
9780      */
9781     El.fly = function(el, named){
9782         named = named || '_global';
9783         el = Roo.getDom(el);
9784         if(!el){
9785             return null;
9786         }
9787         if(!El._flyweights[named]){
9788             El._flyweights[named] = new El.Flyweight();
9789         }
9790         El._flyweights[named].dom = el;
9791         return El._flyweights[named];
9792     };
9793
9794     /**
9795      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9796      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9797      * Shorthand of {@link Roo.Element#get}
9798      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9799      * @return {Element} The Element object
9800      * @member Roo
9801      * @method get
9802      */
9803     Roo.get = El.get;
9804     /**
9805      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9806      * the dom node can be overwritten by other code.
9807      * Shorthand of {@link Roo.Element#fly}
9808      * @param {String/HTMLElement} el The dom node or id
9809      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9810      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9811      * @static
9812      * @return {Element} The shared Element object
9813      * @member Roo
9814      * @method fly
9815      */
9816     Roo.fly = El.fly;
9817
9818     // speedy lookup for elements never to box adjust
9819     var noBoxAdjust = Roo.isStrict ? {
9820         select:1
9821     } : {
9822         input:1, select:1, textarea:1
9823     };
9824     if(Roo.isIE || Roo.isGecko){
9825         noBoxAdjust['button'] = 1;
9826     }
9827
9828
9829     Roo.EventManager.on(window, 'unload', function(){
9830         delete El.cache;
9831         delete El._flyweights;
9832     });
9833 })();
9834
9835
9836
9837
9838 if(Roo.DomQuery){
9839     Roo.Element.selectorFunction = Roo.DomQuery.select;
9840 }
9841
9842 Roo.Element.select = function(selector, unique, root){
9843     var els;
9844     if(typeof selector == "string"){
9845         els = Roo.Element.selectorFunction(selector, root);
9846     }else if(selector.length !== undefined){
9847         els = selector;
9848     }else{
9849         throw "Invalid selector";
9850     }
9851     if(unique === true){
9852         return new Roo.CompositeElement(els);
9853     }else{
9854         return new Roo.CompositeElementLite(els);
9855     }
9856 };
9857 /**
9858  * Selects elements based on the passed CSS selector to enable working on them as 1.
9859  * @param {String/Array} selector The CSS selector or an array of elements
9860  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9861  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9862  * @return {CompositeElementLite/CompositeElement}
9863  * @member Roo
9864  * @method select
9865  */
9866 Roo.select = Roo.Element.select;
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881 /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893
9894 //Notifies Element that fx methods are available
9895 Roo.enableFx = true;
9896
9897 /**
9898  * @class Roo.Fx
9899  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9900  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9901  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9902  * Element effects to work.</p><br/>
9903  *
9904  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9905  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9906  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9907  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9908  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9909  * expected results and should be done with care.</p><br/>
9910  *
9911  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9912  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9913 <pre>
9914 Value  Description
9915 -----  -----------------------------
9916 tl     The top left corner
9917 t      The center of the top edge
9918 tr     The top right corner
9919 l      The center of the left edge
9920 r      The center of the right edge
9921 bl     The bottom left corner
9922 b      The center of the bottom edge
9923 br     The bottom right corner
9924 </pre>
9925  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9926  * below are common options that can be passed to any Fx method.</b>
9927  * @cfg {Function} callback A function called when the effect is finished
9928  * @cfg {Object} scope The scope of the effect function
9929  * @cfg {String} easing A valid Easing value for the effect
9930  * @cfg {String} afterCls A css class to apply after the effect
9931  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9932  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9933  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9934  * effects that end with the element being visually hidden, ignored otherwise)
9935  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9936  * a function which returns such a specification that will be applied to the Element after the effect finishes
9937  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9938  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9939  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9940  */
9941 Roo.Fx = {
9942         /**
9943          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9944          * origin for the slide effect.  This function automatically handles wrapping the element with
9945          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element in from the top
9949 el.slideIn();
9950
9951 // custom: slide the element in from the right with a 2-second duration
9952 el.slideIn('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideIn('t', {
9956     easing: 'easeOut',
9957     duration: .5
9958 });
9959 </code></pre>
9960          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9961          * @param {Object} options (optional) Object literal with any of the Fx config options
9962          * @return {Roo.Element} The Element
9963          */
9964     slideIn : function(anchor, o){
9965         var el = this.getFxEl();
9966         o = o || {};
9967
9968         el.queueFx(o, function(){
9969
9970             anchor = anchor || "t";
9971
9972             // fix display to visibility
9973             this.fixDisplay();
9974
9975             // restore values after effect
9976             var r = this.getFxRestore();
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "hidden");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             // clear out temp styles after slide and unwrap
9989             var after = function(){
9990                 el.fxUnwrap(wrap, r.pos, o);
9991                 st.width = r.width;
9992                 st.height = r.height;
9993                 el.afterFx(o);
9994             };
9995             // time to calc the positions
9996             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9997
9998             switch(anchor.toLowerCase()){
9999                 case "t":
10000                     wrap.setSize(b.width, 0);
10001                     st.left = st.bottom = "0";
10002                     a = {height: bh};
10003                 break;
10004                 case "l":
10005                     wrap.setSize(0, b.height);
10006                     st.right = st.top = "0";
10007                     a = {width: bw};
10008                 break;
10009                 case "r":
10010                     wrap.setSize(0, b.height);
10011                     wrap.setX(b.right);
10012                     st.left = st.top = "0";
10013                     a = {width: bw, points: pt};
10014                 break;
10015                 case "b":
10016                     wrap.setSize(b.width, 0);
10017                     wrap.setY(b.bottom);
10018                     st.left = st.top = "0";
10019                     a = {height: bh, points: pt};
10020                 break;
10021                 case "tl":
10022                     wrap.setSize(0, 0);
10023                     st.right = st.bottom = "0";
10024                     a = {width: bw, height: bh};
10025                 break;
10026                 case "bl":
10027                     wrap.setSize(0, 0);
10028                     wrap.setY(b.y+b.height);
10029                     st.right = st.top = "0";
10030                     a = {width: bw, height: bh, points: pt};
10031                 break;
10032                 case "br":
10033                     wrap.setSize(0, 0);
10034                     wrap.setXY([b.right, b.bottom]);
10035                     st.left = st.top = "0";
10036                     a = {width: bw, height: bh, points: pt};
10037                 break;
10038                 case "tr":
10039                     wrap.setSize(0, 0);
10040                     wrap.setX(b.x+b.width);
10041                     st.left = st.bottom = "0";
10042                     a = {width: bw, height: bh, points: pt};
10043                 break;
10044             }
10045             this.dom.style.visibility = "visible";
10046             wrap.show();
10047
10048             arguments.callee.anim = wrap.fxanim(a,
10049                 o,
10050                 'motion',
10051                 .5,
10052                 'easeOut', after);
10053         });
10054         return this;
10055     },
10056     
10057         /**
10058          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10059          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10060          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10061          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10062          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10063          * Usage:
10064          *<pre><code>
10065 // default: slide the element out to the top
10066 el.slideOut();
10067
10068 // custom: slide the element out to the right with a 2-second duration
10069 el.slideOut('r', { duration: 2 });
10070
10071 // common config options shown with default values
10072 el.slideOut('t', {
10073     easing: 'easeOut',
10074     duration: .5,
10075     remove: false,
10076     useDisplay: false
10077 });
10078 </code></pre>
10079          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10080          * @param {Object} options (optional) Object literal with any of the Fx config options
10081          * @return {Roo.Element} The Element
10082          */
10083     slideOut : function(anchor, o){
10084         var el = this.getFxEl();
10085         o = o || {};
10086
10087         el.queueFx(o, function(){
10088
10089             anchor = anchor || "t";
10090
10091             // restore values after effect
10092             var r = this.getFxRestore();
10093             
10094             var b = this.getBox();
10095             // fixed size for slide
10096             this.setSize(b);
10097
10098             // wrap if needed
10099             var wrap = this.fxWrap(r.pos, o, "visible");
10100
10101             var st = this.dom.style;
10102             st.visibility = "visible";
10103             st.position = "absolute";
10104
10105             wrap.setSize(b);
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.fxUnwrap(wrap, r.pos, o);
10115
10116                 st.width = r.width;
10117                 st.height = r.height;
10118
10119                 el.afterFx(o);
10120             };
10121
10122             var a, zero = {to: 0};
10123             switch(anchor.toLowerCase()){
10124                 case "t":
10125                     st.left = st.bottom = "0";
10126                     a = {height: zero};
10127                 break;
10128                 case "l":
10129                     st.right = st.top = "0";
10130                     a = {width: zero};
10131                 break;
10132                 case "r":
10133                     st.left = st.top = "0";
10134                     a = {width: zero, points: {to:[b.right, b.y]}};
10135                 break;
10136                 case "b":
10137                     st.left = st.top = "0";
10138                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "tl":
10141                     st.right = st.bottom = "0";
10142                     a = {width: zero, height: zero};
10143                 break;
10144                 case "bl":
10145                     st.right = st.top = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "br":
10149                     st.left = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10151                 break;
10152                 case "tr":
10153                     st.left = st.bottom = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10155                 break;
10156             }
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 "easeOut", after);
10163         });
10164         return this;
10165     },
10166
10167         /**
10168          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10169          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10170          * The element must be removed from the DOM using the 'remove' config option if desired.
10171          * Usage:
10172          *<pre><code>
10173 // default
10174 el.puff();
10175
10176 // common config options shown with default values
10177 el.puff({
10178     easing: 'easeOut',
10179     duration: .5,
10180     remove: false,
10181     useDisplay: false
10182 });
10183 </code></pre>
10184          * @param {Object} options (optional) Object literal with any of the Fx config options
10185          * @return {Roo.Element} The Element
10186          */
10187     puff : function(o){
10188         var el = this.getFxEl();
10189         o = o || {};
10190
10191         el.queueFx(o, function(){
10192             this.clearOpacity();
10193             this.show();
10194
10195             // restore values after effect
10196             var r = this.getFxRestore();
10197             var st = this.dom.style;
10198
10199             var after = function(){
10200                 if(o.useDisplay){
10201                     el.setDisplayed(false);
10202                 }else{
10203                     el.hide();
10204                 }
10205
10206                 el.clearOpacity();
10207
10208                 el.setPositioning(r.pos);
10209                 st.width = r.width;
10210                 st.height = r.height;
10211                 st.fontSize = '';
10212                 el.afterFx(o);
10213             };
10214
10215             var width = this.getWidth();
10216             var height = this.getHeight();
10217
10218             arguments.callee.anim = this.fxanim({
10219                     width : {to: this.adjustWidth(width * 2)},
10220                     height : {to: this.adjustHeight(height * 2)},
10221                     points : {by: [-(width * .5), -(height * .5)]},
10222                     opacity : {to: 0},
10223                     fontSize: {to:200, unit: "%"}
10224                 },
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10235          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10236          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.switchOff();
10241
10242 // all config options shown with default values
10243 el.switchOff({
10244     easing: 'easeIn',
10245     duration: .3,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     switchOff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.clip();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273                 el.setPositioning(r.pos);
10274                 st.width = r.width;
10275                 st.height = r.height;
10276
10277                 el.afterFx(o);
10278             };
10279
10280             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10281                 this.clearOpacity();
10282                 (function(){
10283                     this.fxanim({
10284                         height:{to:1},
10285                         points:{by:[0, this.getHeight() * .5]}
10286                     }, o, 'motion', 0.3, 'easeIn', after);
10287                 }).defer(100, this);
10288             });
10289         });
10290         return this;
10291     },
10292
10293     /**
10294      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10295      * changed using the "attr" config option) and then fading back to the original color. If no original
10296      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10297      * Usage:
10298 <pre><code>
10299 // default: highlight background to yellow
10300 el.highlight();
10301
10302 // custom: highlight foreground text to blue for 2 seconds
10303 el.highlight("0000ff", { attr: 'color', duration: 2 });
10304
10305 // common config options shown with default values
10306 el.highlight("ffff9c", {
10307     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10308     endColor: (current color) or "ffffff",
10309     easing: 'easeIn',
10310     duration: 1
10311 });
10312 </code></pre>
10313      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10314      * @param {Object} options (optional) Object literal with any of the Fx config options
10315      * @return {Roo.Element} The Element
10316      */ 
10317     highlight : function(color, o){
10318         var el = this.getFxEl();
10319         o = o || {};
10320
10321         el.queueFx(o, function(){
10322             color = color || "ffff9c";
10323             attr = o.attr || "backgroundColor";
10324
10325             this.clearOpacity();
10326             this.show();
10327
10328             var origColor = this.getColor(attr);
10329             var restoreColor = this.dom.style[attr];
10330             endColor = (o.endColor || origColor) || "ffffff";
10331
10332             var after = function(){
10333                 el.dom.style[attr] = restoreColor;
10334                 el.afterFx(o);
10335             };
10336
10337             var a = {};
10338             a[attr] = {from: color, to: endColor};
10339             arguments.callee.anim = this.fxanim(a,
10340                 o,
10341                 'color',
10342                 1,
10343                 'easeIn', after);
10344         });
10345         return this;
10346     },
10347
10348    /**
10349     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10350     * Usage:
10351 <pre><code>
10352 // default: a single light blue ripple
10353 el.frame();
10354
10355 // custom: 3 red ripples lasting 3 seconds total
10356 el.frame("ff0000", 3, { duration: 3 });
10357
10358 // common config options shown with default values
10359 el.frame("C3DAF9", 1, {
10360     duration: 1 //duration of entire animation (not each individual ripple)
10361     // Note: Easing is not configurable and will be ignored if included
10362 });
10363 </code></pre>
10364     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10365     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10366     * @param {Object} options (optional) Object literal with any of the Fx config options
10367     * @return {Roo.Element} The Element
10368     */
10369     frame : function(color, count, o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             color = color || "#C3DAF9";
10375             if(color.length == 6){
10376                 color = "#" + color;
10377             }
10378             count = count || 1;
10379             duration = o.duration || 1;
10380             this.show();
10381
10382             var b = this.getBox();
10383             var animFn = function(){
10384                 var proxy = this.createProxy({
10385
10386                      style:{
10387                         visbility:"hidden",
10388                         position:"absolute",
10389                         "z-index":"35000", // yee haw
10390                         border:"0px solid " + color
10391                      }
10392                   });
10393                 var scale = Roo.isBorderBox ? 2 : 1;
10394                 proxy.animate({
10395                     top:{from:b.y, to:b.y - 20},
10396                     left:{from:b.x, to:b.x - 20},
10397                     borderWidth:{from:0, to:10},
10398                     opacity:{from:1, to:0},
10399                     height:{from:b.height, to:(b.height + (20*scale))},
10400                     width:{from:b.width, to:(b.width + (20*scale))}
10401                 }, duration, function(){
10402                     proxy.remove();
10403                 });
10404                 if(--count > 0){
10405                      animFn.defer((duration/2)*1000, this);
10406                 }else{
10407                     el.afterFx(o);
10408                 }
10409             };
10410             animFn.call(this);
10411         });
10412         return this;
10413     },
10414
10415    /**
10416     * Creates a pause before any subsequent queued effects begin.  If there are
10417     * no effects queued after the pause it will have no effect.
10418     * Usage:
10419 <pre><code>
10420 el.pause(1);
10421 </code></pre>
10422     * @param {Number} seconds The length of time to pause (in seconds)
10423     * @return {Roo.Element} The Element
10424     */
10425     pause : function(seconds){
10426         var el = this.getFxEl();
10427         var o = {};
10428
10429         el.queueFx(o, function(){
10430             setTimeout(function(){
10431                 el.afterFx(o);
10432             }, seconds * 1000);
10433         });
10434         return this;
10435     },
10436
10437    /**
10438     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10439     * using the "endOpacity" config option.
10440     * Usage:
10441 <pre><code>
10442 // default: fade in from opacity 0 to 100%
10443 el.fadeIn();
10444
10445 // custom: fade in from opacity 0 to 75% over 2 seconds
10446 el.fadeIn({ endOpacity: .75, duration: 2});
10447
10448 // common config options shown with default values
10449 el.fadeIn({
10450     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10451     easing: 'easeOut',
10452     duration: .5
10453 });
10454 </code></pre>
10455     * @param {Object} options (optional) Object literal with any of the Fx config options
10456     * @return {Roo.Element} The Element
10457     */
10458     fadeIn : function(o){
10459         var el = this.getFxEl();
10460         o = o || {};
10461         el.queueFx(o, function(){
10462             this.setOpacity(0);
10463             this.fixDisplay();
10464             this.dom.style.visibility = 'visible';
10465             var to = o.endOpacity || 1;
10466             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10467                 o, null, .5, "easeOut", function(){
10468                 if(to == 1){
10469                     this.clearOpacity();
10470                 }
10471                 el.afterFx(o);
10472             });
10473         });
10474         return this;
10475     },
10476
10477    /**
10478     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10479     * using the "endOpacity" config option.
10480     * Usage:
10481 <pre><code>
10482 // default: fade out from the element's current opacity to 0
10483 el.fadeOut();
10484
10485 // custom: fade out from the element's current opacity to 25% over 2 seconds
10486 el.fadeOut({ endOpacity: .25, duration: 2});
10487
10488 // common config options shown with default values
10489 el.fadeOut({
10490     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10491     easing: 'easeOut',
10492     duration: .5
10493     remove: false,
10494     useDisplay: false
10495 });
10496 </code></pre>
10497     * @param {Object} options (optional) Object literal with any of the Fx config options
10498     * @return {Roo.Element} The Element
10499     */
10500     fadeOut : function(o){
10501         var el = this.getFxEl();
10502         o = o || {};
10503         el.queueFx(o, function(){
10504             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10505                 o, null, .5, "easeOut", function(){
10506                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10507                      this.dom.style.display = "none";
10508                 }else{
10509                      this.dom.style.visibility = "hidden";
10510                 }
10511                 this.clearOpacity();
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Animates the transition of an element's dimensions from a starting height/width
10520     * to an ending height/width.
10521     * Usage:
10522 <pre><code>
10523 // change height and width to 100x100 pixels
10524 el.scale(100, 100);
10525
10526 // common config options shown with default values.  The height and width will default to
10527 // the element's existing values if passed as null.
10528 el.scale(
10529     [element's width],
10530     [element's height], {
10531     easing: 'easeOut',
10532     duration: .35
10533 });
10534 </code></pre>
10535     * @param {Number} width  The new width (pass undefined to keep the original width)
10536     * @param {Number} height  The new height (pass undefined to keep the original height)
10537     * @param {Object} options (optional) Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     scale : function(w, h, o){
10541         this.shift(Roo.apply({}, o, {
10542             width: w,
10543             height: h
10544         }));
10545         return this;
10546     },
10547
10548    /**
10549     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10550     * Any of these properties not specified in the config object will not be changed.  This effect 
10551     * requires that at least one new dimension, position or opacity setting must be passed in on
10552     * the config object in order for the function to have any effect.
10553     * Usage:
10554 <pre><code>
10555 // slide the element horizontally to x position 200 while changing the height and opacity
10556 el.shift({ x: 200, height: 50, opacity: .8 });
10557
10558 // common config options shown with default values.
10559 el.shift({
10560     width: [element's width],
10561     height: [element's height],
10562     x: [element's x position],
10563     y: [element's y position],
10564     opacity: [element's opacity],
10565     easing: 'easeOut',
10566     duration: .35
10567 });
10568 </code></pre>
10569     * @param {Object} options  Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     shift : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10577             if(w !== undefined){
10578                 a.width = {to: this.adjustWidth(w)};
10579             }
10580             if(h !== undefined){
10581                 a.height = {to: this.adjustHeight(h)};
10582             }
10583             if(x !== undefined || y !== undefined){
10584                 a.points = {to: [
10585                     x !== undefined ? x : this.getX(),
10586                     y !== undefined ? y : this.getY()
10587                 ]};
10588             }
10589             if(op !== undefined){
10590                 a.opacity = {to: op};
10591             }
10592             if(o.xy !== undefined){
10593                 a.points = {to: o.xy};
10594             }
10595             arguments.callee.anim = this.fxanim(a,
10596                 o, 'motion', .35, "easeOut", function(){
10597                 el.afterFx(o);
10598             });
10599         });
10600         return this;
10601     },
10602
10603         /**
10604          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10605          * ending point of the effect.
10606          * Usage:
10607          *<pre><code>
10608 // default: slide the element downward while fading out
10609 el.ghost();
10610
10611 // custom: slide the element out to the right with a 2-second duration
10612 el.ghost('r', { duration: 2 });
10613
10614 // common config options shown with default values
10615 el.ghost('b', {
10616     easing: 'easeOut',
10617     duration: .5
10618     remove: false,
10619     useDisplay: false
10620 });
10621 </code></pre>
10622          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10623          * @param {Object} options (optional) Object literal with any of the Fx config options
10624          * @return {Roo.Element} The Element
10625          */
10626     ghost : function(anchor, o){
10627         var el = this.getFxEl();
10628         o = o || {};
10629
10630         el.queueFx(o, function(){
10631             anchor = anchor || "b";
10632
10633             // restore values after effect
10634             var r = this.getFxRestore();
10635             var w = this.getWidth(),
10636                 h = this.getHeight();
10637
10638             var st = this.dom.style;
10639
10640             var after = function(){
10641                 if(o.useDisplay){
10642                     el.setDisplayed(false);
10643                 }else{
10644                     el.hide();
10645                 }
10646
10647                 el.clearOpacity();
10648                 el.setPositioning(r.pos);
10649                 st.width = r.width;
10650                 st.height = r.height;
10651
10652                 el.afterFx(o);
10653             };
10654
10655             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10656             switch(anchor.toLowerCase()){
10657                 case "t":
10658                     pt.by = [0, -h];
10659                 break;
10660                 case "l":
10661                     pt.by = [-w, 0];
10662                 break;
10663                 case "r":
10664                     pt.by = [w, 0];
10665                 break;
10666                 case "b":
10667                     pt.by = [0, h];
10668                 break;
10669                 case "tl":
10670                     pt.by = [-w, -h];
10671                 break;
10672                 case "bl":
10673                     pt.by = [-w, h];
10674                 break;
10675                 case "br":
10676                     pt.by = [w, h];
10677                 break;
10678                 case "tr":
10679                     pt.by = [w, -h];
10680                 break;
10681             }
10682
10683             arguments.callee.anim = this.fxanim(a,
10684                 o,
10685                 'motion',
10686                 .5,
10687                 "easeOut", after);
10688         });
10689         return this;
10690     },
10691
10692         /**
10693          * Ensures that all effects queued after syncFx is called on the element are
10694          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10695          * @return {Roo.Element} The Element
10696          */
10697     syncFx : function(){
10698         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10699             block : false,
10700             concurrent : true,
10701             stopFx : false
10702         });
10703         return this;
10704     },
10705
10706         /**
10707          * Ensures that all effects queued after sequenceFx is called on the element are
10708          * run in sequence.  This is the opposite of {@link #syncFx}.
10709          * @return {Roo.Element} The Element
10710          */
10711     sequenceFx : function(){
10712         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10713             block : false,
10714             concurrent : false,
10715             stopFx : false
10716         });
10717         return this;
10718     },
10719
10720         /* @private */
10721     nextFx : function(){
10722         var ef = this.fxQueue[0];
10723         if(ef){
10724             ef.call(this);
10725         }
10726     },
10727
10728         /**
10729          * Returns true if the element has any effects actively running or queued, else returns false.
10730          * @return {Boolean} True if element has active effects, else false
10731          */
10732     hasActiveFx : function(){
10733         return this.fxQueue && this.fxQueue[0];
10734     },
10735
10736         /**
10737          * Stops any running effects and clears the element's internal effects queue if it contains
10738          * any additional effects that haven't started yet.
10739          * @return {Roo.Element} The Element
10740          */
10741     stopFx : function(){
10742         if(this.hasActiveFx()){
10743             var cur = this.fxQueue[0];
10744             if(cur && cur.anim && cur.anim.isAnimated()){
10745                 this.fxQueue = [cur]; // clear out others
10746                 cur.anim.stop(true);
10747             }
10748         }
10749         return this;
10750     },
10751
10752         /* @private */
10753     beforeFx : function(o){
10754         if(this.hasActiveFx() && !o.concurrent){
10755            if(o.stopFx){
10756                this.stopFx();
10757                return true;
10758            }
10759            return false;
10760         }
10761         return true;
10762     },
10763
10764         /**
10765          * Returns true if the element is currently blocking so that no other effect can be queued
10766          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10767          * used to ensure that an effect initiated by a user action runs to completion prior to the
10768          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10769          * @return {Boolean} True if blocking, else false
10770          */
10771     hasFxBlock : function(){
10772         var q = this.fxQueue;
10773         return q && q[0] && q[0].block;
10774     },
10775
10776         /* @private */
10777     queueFx : function(o, fn){
10778         if(!this.fxQueue){
10779             this.fxQueue = [];
10780         }
10781         if(!this.hasFxBlock()){
10782             Roo.applyIf(o, this.fxDefaults);
10783             if(!o.concurrent){
10784                 var run = this.beforeFx(o);
10785                 fn.block = o.block;
10786                 this.fxQueue.push(fn);
10787                 if(run){
10788                     this.nextFx();
10789                 }
10790             }else{
10791                 fn.call(this);
10792             }
10793         }
10794         return this;
10795     },
10796
10797         /* @private */
10798     fxWrap : function(pos, o, vis){
10799         var wrap;
10800         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10801             var wrapXY;
10802             if(o.fixPosition){
10803                 wrapXY = this.getXY();
10804             }
10805             var div = document.createElement("div");
10806             div.style.visibility = vis;
10807             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10808             wrap.setPositioning(pos);
10809             if(wrap.getStyle("position") == "static"){
10810                 wrap.position("relative");
10811             }
10812             this.clearPositioning('auto');
10813             wrap.clip();
10814             wrap.dom.appendChild(this.dom);
10815             if(wrapXY){
10816                 wrap.setXY(wrapXY);
10817             }
10818         }
10819         return wrap;
10820     },
10821
10822         /* @private */
10823     fxUnwrap : function(wrap, pos, o){
10824         this.clearPositioning();
10825         this.setPositioning(pos);
10826         if(!o.wrap){
10827             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10828             wrap.remove();
10829         }
10830     },
10831
10832         /* @private */
10833     getFxRestore : function(){
10834         var st = this.dom.style;
10835         return {pos: this.getPositioning(), width: st.width, height : st.height};
10836     },
10837
10838         /* @private */
10839     afterFx : function(o){
10840         if(o.afterStyle){
10841             this.applyStyles(o.afterStyle);
10842         }
10843         if(o.afterCls){
10844             this.addClass(o.afterCls);
10845         }
10846         if(o.remove === true){
10847             this.remove();
10848         }
10849         Roo.callback(o.callback, o.scope, [this]);
10850         if(!o.concurrent){
10851             this.fxQueue.shift();
10852             this.nextFx();
10853         }
10854     },
10855
10856         /* @private */
10857     getFxEl : function(){ // support for composite element fx
10858         return Roo.get(this.dom);
10859     },
10860
10861         /* @private */
10862     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10863         animType = animType || 'run';
10864         opt = opt || {};
10865         var anim = Roo.lib.Anim[animType](
10866             this.dom, args,
10867             (opt.duration || defaultDur) || .35,
10868             (opt.easing || defaultEase) || 'easeOut',
10869             function(){
10870                 Roo.callback(cb, this);
10871             },
10872             this
10873         );
10874         opt.anim = anim;
10875         return anim;
10876     }
10877 };
10878
10879 // backwords compat
10880 Roo.Fx.resize = Roo.Fx.scale;
10881
10882 //When included, Roo.Fx is automatically applied to Element so that all basic
10883 //effects are available directly via the Element API
10884 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10885  * Based on:
10886  * Ext JS Library 1.1.1
10887  * Copyright(c) 2006-2007, Ext JS, LLC.
10888  *
10889  * Originally Released Under LGPL - original licence link has changed is not relivant.
10890  *
10891  * Fork - LGPL
10892  * <script type="text/javascript">
10893  */
10894
10895
10896 /**
10897  * @class Roo.CompositeElement
10898  * Standard composite class. Creates a Roo.Element for every element in the collection.
10899  * <br><br>
10900  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10901  * actions will be performed on all the elements in this collection.</b>
10902  * <br><br>
10903  * All methods return <i>this</i> and can be chained.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class", true);
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class', true);
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre>
10915  */
10916 Roo.CompositeElement = function(els){
10917     this.elements = [];
10918     this.addElements(els);
10919 };
10920 Roo.CompositeElement.prototype = {
10921     isComposite: true,
10922     addElements : function(els){
10923         if(!els) return this;
10924         if(typeof els == "string"){
10925             els = Roo.Element.selectorFunction(els);
10926         }
10927         var yels = this.elements;
10928         var index = yels.length-1;
10929         for(var i = 0, len = els.length; i < len; i++) {
10930                 yels[++index] = Roo.get(els[i]);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Clears this composite and adds the elements returned by the passed selector.
10937     * @param {String/Array} els A string CSS selector, an array of elements or an element
10938     * @return {CompositeElement} this
10939     */
10940     fill : function(els){
10941         this.elements = [];
10942         this.add(els);
10943         return this;
10944     },
10945
10946     /**
10947     * Filters this composite to only elements that match the passed selector.
10948     * @param {String} selector A string CSS selector
10949     * @return {CompositeElement} this
10950     */
10951     filter : function(selector){
10952         var els = [];
10953         this.each(function(el){
10954             if(el.is(selector)){
10955                 els[els.length] = el.dom;
10956             }
10957         });
10958         this.fill(els);
10959         return this;
10960     },
10961
10962     invoke : function(fn, args){
10963         var els = this.elements;
10964         for(var i = 0, len = els.length; i < len; i++) {
10965                 Roo.Element.prototype[fn].apply(els[i], args);
10966         }
10967         return this;
10968     },
10969     /**
10970     * Adds elements to this composite.
10971     * @param {String/Array} els A string CSS selector, an array of elements or an element
10972     * @return {CompositeElement} this
10973     */
10974     add : function(els){
10975         if(typeof els == "string"){
10976             this.addElements(Roo.Element.selectorFunction(els));
10977         }else if(els.length !== undefined){
10978             this.addElements(els);
10979         }else{
10980             this.addElements([els]);
10981         }
10982         return this;
10983     },
10984     /**
10985     * Calls the passed function passing (el, this, index) for each element in this composite.
10986     * @param {Function} fn The function to call
10987     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10988     * @return {CompositeElement} this
10989     */
10990     each : function(fn, scope){
10991         var els = this.elements;
10992         for(var i = 0, len = els.length; i < len; i++){
10993             if(fn.call(scope || els[i], els[i], this, i) === false) {
10994                 break;
10995             }
10996         }
10997         return this;
10998     },
10999
11000     /**
11001      * Returns the Element object at the specified index
11002      * @param {Number} index
11003      * @return {Roo.Element}
11004      */
11005     item : function(index){
11006         return this.elements[index] || null;
11007     },
11008
11009     /**
11010      * Returns the first Element
11011      * @return {Roo.Element}
11012      */
11013     first : function(){
11014         return this.item(0);
11015     },
11016
11017     /**
11018      * Returns the last Element
11019      * @return {Roo.Element}
11020      */
11021     last : function(){
11022         return this.item(this.elements.length-1);
11023     },
11024
11025     /**
11026      * Returns the number of elements in this composite
11027      * @return Number
11028      */
11029     getCount : function(){
11030         return this.elements.length;
11031     },
11032
11033     /**
11034      * Returns true if this composite contains the passed element
11035      * @return Boolean
11036      */
11037     contains : function(el){
11038         return this.indexOf(el) !== -1;
11039     },
11040
11041     /**
11042      * Returns true if this composite contains the passed element
11043      * @return Boolean
11044      */
11045     indexOf : function(el){
11046         return this.elements.indexOf(Roo.get(el));
11047     },
11048
11049
11050     /**
11051     * Removes the specified element(s).
11052     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11053     * or an array of any of those.
11054     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11055     * @return {CompositeElement} this
11056     */
11057     removeElement : function(el, removeDom){
11058         if(el instanceof Array){
11059             for(var i = 0, len = el.length; i < len; i++){
11060                 this.removeElement(el[i]);
11061             }
11062             return this;
11063         }
11064         var index = typeof el == 'number' ? el : this.indexOf(el);
11065         if(index !== -1){
11066             if(removeDom){
11067                 var d = this.elements[index];
11068                 if(d.dom){
11069                     d.remove();
11070                 }else{
11071                     d.parentNode.removeChild(d);
11072                 }
11073             }
11074             this.elements.splice(index, 1);
11075         }
11076         return this;
11077     },
11078
11079     /**
11080     * Replaces the specified element with the passed element.
11081     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11082     * to replace.
11083     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11084     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11085     * @return {CompositeElement} this
11086     */
11087     replaceElement : function(el, replacement, domReplace){
11088         var index = typeof el == 'number' ? el : this.indexOf(el);
11089         if(index !== -1){
11090             if(domReplace){
11091                 this.elements[index].replaceWith(replacement);
11092             }else{
11093                 this.elements.splice(index, 1, Roo.get(replacement))
11094             }
11095         }
11096         return this;
11097     },
11098
11099     /**
11100      * Removes all elements.
11101      */
11102     clear : function(){
11103         this.elements = [];
11104     }
11105 };
11106 (function(){
11107     Roo.CompositeElement.createCall = function(proto, fnName){
11108         if(!proto[fnName]){
11109             proto[fnName] = function(){
11110                 return this.invoke(fnName, arguments);
11111             };
11112         }
11113     };
11114     for(var fnName in Roo.Element.prototype){
11115         if(typeof Roo.Element.prototype[fnName] == "function"){
11116             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11117         }
11118     };
11119 })();
11120 /*
11121  * Based on:
11122  * Ext JS Library 1.1.1
11123  * Copyright(c) 2006-2007, Ext JS, LLC.
11124  *
11125  * Originally Released Under LGPL - original licence link has changed is not relivant.
11126  *
11127  * Fork - LGPL
11128  * <script type="text/javascript">
11129  */
11130
11131 /**
11132  * @class Roo.CompositeElementLite
11133  * @extends Roo.CompositeElement
11134  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11135  <pre><code>
11136  var els = Roo.select("#some-el div.some-class");
11137  // or select directly from an existing element
11138  var el = Roo.get('some-el');
11139  el.select('div.some-class');
11140
11141  els.setWidth(100); // all elements become 100 width
11142  els.hide(true); // all elements fade out and hide
11143  // or
11144  els.setWidth(100).hide(true);
11145  </code></pre><br><br>
11146  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11147  * actions will be performed on all the elements in this collection.</b>
11148  */
11149 Roo.CompositeElementLite = function(els){
11150     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11151     this.el = new Roo.Element.Flyweight();
11152 };
11153 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11154     addElements : function(els){
11155         if(els){
11156             if(els instanceof Array){
11157                 this.elements = this.elements.concat(els);
11158             }else{
11159                 var yels = this.elements;
11160                 var index = yels.length-1;
11161                 for(var i = 0, len = els.length; i < len; i++) {
11162                     yels[++index] = els[i];
11163                 }
11164             }
11165         }
11166         return this;
11167     },
11168     invoke : function(fn, args){
11169         var els = this.elements;
11170         var el = this.el;
11171         for(var i = 0, len = els.length; i < len; i++) {
11172             el.dom = els[i];
11173                 Roo.Element.prototype[fn].apply(el, args);
11174         }
11175         return this;
11176     },
11177     /**
11178      * Returns a flyweight Element of the dom element object at the specified index
11179      * @param {Number} index
11180      * @return {Roo.Element}
11181      */
11182     item : function(index){
11183         if(!this.elements[index]){
11184             return null;
11185         }
11186         this.el.dom = this.elements[index];
11187         return this.el;
11188     },
11189
11190     // fixes scope with flyweight
11191     addListener : function(eventName, handler, scope, opt){
11192         var els = this.elements;
11193         for(var i = 0, len = els.length; i < len; i++) {
11194             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11195         }
11196         return this;
11197     },
11198
11199     /**
11200     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11201     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11202     * a reference to the dom node, use el.dom.</b>
11203     * @param {Function} fn The function to call
11204     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11205     * @return {CompositeElement} this
11206     */
11207     each : function(fn, scope){
11208         var els = this.elements;
11209         var el = this.el;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             el.dom = els[i];
11212                 if(fn.call(scope || el, el, this, i) === false){
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     indexOf : function(el){
11220         return this.elements.indexOf(Roo.getDom(el));
11221     },
11222
11223     replaceElement : function(el, replacement, domReplace){
11224         var index = typeof el == 'number' ? el : this.indexOf(el);
11225         if(index !== -1){
11226             replacement = Roo.getDom(replacement);
11227             if(domReplace){
11228                 var d = this.elements[index];
11229                 d.parentNode.insertBefore(replacement, d);
11230                 d.parentNode.removeChild(d);
11231             }
11232             this.elements.splice(index, 1, replacement);
11233         }
11234         return this;
11235     }
11236 });
11237 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11238
11239 /*
11240  * Based on:
11241  * Ext JS Library 1.1.1
11242  * Copyright(c) 2006-2007, Ext JS, LLC.
11243  *
11244  * Originally Released Under LGPL - original licence link has changed is not relivant.
11245  *
11246  * Fork - LGPL
11247  * <script type="text/javascript">
11248  */
11249
11250  
11251
11252 /**
11253  * @class Roo.data.Connection
11254  * @extends Roo.util.Observable
11255  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11256  * either to a configured URL, or to a URL specified at request time.<br><br>
11257  * <p>
11258  * Requests made by this class are asynchronous, and will return immediately. No data from
11259  * the server will be available to the statement immediately following the {@link #request} call.
11260  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11261  * <p>
11262  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11263  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11264  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11265  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11266  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11267  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11268  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11269  * standard DOM methods.
11270  * @constructor
11271  * @param {Object} config a configuration object.
11272  */
11273 Roo.data.Connection = function(config){
11274     Roo.apply(this, config);
11275     this.addEvents({
11276         /**
11277          * @event beforerequest
11278          * Fires before a network request is made to retrieve a data object.
11279          * @param {Connection} conn This Connection object.
11280          * @param {Object} options The options config object passed to the {@link #request} method.
11281          */
11282         "beforerequest" : true,
11283         /**
11284          * @event requestcomplete
11285          * Fires if the request was successfully completed.
11286          * @param {Connection} conn This Connection object.
11287          * @param {Object} response The XHR object containing the response data.
11288          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11289          * @param {Object} options The options config object passed to the {@link #request} method.
11290          */
11291         "requestcomplete" : true,
11292         /**
11293          * @event requestexception
11294          * Fires if an error HTTP status was returned from the server.
11295          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11296          * @param {Connection} conn This Connection object.
11297          * @param {Object} response The XHR object containing the response data.
11298          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11299          * @param {Object} options The options config object passed to the {@link #request} method.
11300          */
11301         "requestexception" : true
11302     });
11303     Roo.data.Connection.superclass.constructor.call(this);
11304 };
11305
11306 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11307     /**
11308      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11309      */
11310     /**
11311      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11312      * extra parameters to each request made by this object. (defaults to undefined)
11313      */
11314     /**
11315      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11316      *  to each request made by this object. (defaults to undefined)
11317      */
11318     /**
11319      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11320      */
11321     /**
11322      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11323      */
11324     timeout : 30000,
11325     /**
11326      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11327      * @type Boolean
11328      */
11329     autoAbort:false,
11330
11331     /**
11332      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11333      * @type Boolean
11334      */
11335     disableCaching: true,
11336
11337     /**
11338      * Sends an HTTP request to a remote server.
11339      * @param {Object} options An object which may contain the following properties:<ul>
11340      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11341      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11342      * request, a url encoded string or a function to call to get either.</li>
11343      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11344      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11345      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11346      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11347      * <li>options {Object} The parameter to the request call.</li>
11348      * <li>success {Boolean} True if the request succeeded.</li>
11349      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11350      * </ul></li>
11351      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11352      * The callback is passed the following parameters:<ul>
11353      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11354      * <li>options {Object} The parameter to the request call.</li>
11355      * </ul></li>
11356      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11357      * The callback is passed the following parameters:<ul>
11358      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11359      * <li>options {Object} The parameter to the request call.</li>
11360      * </ul></li>
11361      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11362      * for the callback function. Defaults to the browser window.</li>
11363      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11364      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11365      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11366      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11367      * params for the post data. Any params will be appended to the URL.</li>
11368      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11369      * </ul>
11370      * @return {Number} transactionId
11371      */
11372     request : function(o){
11373         if(this.fireEvent("beforerequest", this, o) !== false){
11374             var p = o.params;
11375
11376             if(typeof p == "function"){
11377                 p = p.call(o.scope||window, o);
11378             }
11379             if(typeof p == "object"){
11380                 p = Roo.urlEncode(o.params);
11381             }
11382             if(this.extraParams){
11383                 var extras = Roo.urlEncode(this.extraParams);
11384                 p = p ? (p + '&' + extras) : extras;
11385             }
11386
11387             var url = o.url || this.url;
11388             if(typeof url == 'function'){
11389                 url = url.call(o.scope||window, o);
11390             }
11391
11392             if(o.form){
11393                 var form = Roo.getDom(o.form);
11394                 url = url || form.action;
11395
11396                 var enctype = form.getAttribute("enctype");
11397                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11398                     return this.doFormUpload(o, p, url);
11399                 }
11400                 var f = Roo.lib.Ajax.serializeForm(form);
11401                 p = p ? (p + '&' + f) : f;
11402             }
11403
11404             var hs = o.headers;
11405             if(this.defaultHeaders){
11406                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11407                 if(!o.headers){
11408                     o.headers = hs;
11409                 }
11410             }
11411
11412             var cb = {
11413                 success: this.handleResponse,
11414                 failure: this.handleFailure,
11415                 scope: this,
11416                 argument: {options: o},
11417                 timeout : o.timeout || this.timeout
11418             };
11419
11420             var method = o.method||this.method||(p ? "POST" : "GET");
11421
11422             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11423                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11424             }
11425
11426             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11427                 if(o.autoAbort){
11428                     this.abort();
11429                 }
11430             }else if(this.autoAbort !== false){
11431                 this.abort();
11432             }
11433
11434             if((method == 'GET' && p) || o.xmlData){
11435                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11436                 p = '';
11437             }
11438             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11439             return this.transId;
11440         }else{
11441             Roo.callback(o.callback, o.scope, [o, null, null]);
11442             return null;
11443         }
11444     },
11445
11446     /**
11447      * Determine whether this object has a request outstanding.
11448      * @param {Number} transactionId (Optional) defaults to the last transaction
11449      * @return {Boolean} True if there is an outstanding request.
11450      */
11451     isLoading : function(transId){
11452         if(transId){
11453             return Roo.lib.Ajax.isCallInProgress(transId);
11454         }else{
11455             return this.transId ? true : false;
11456         }
11457     },
11458
11459     /**
11460      * Aborts any outstanding request.
11461      * @param {Number} transactionId (Optional) defaults to the last transaction
11462      */
11463     abort : function(transId){
11464         if(transId || this.isLoading()){
11465             Roo.lib.Ajax.abort(transId || this.transId);
11466         }
11467     },
11468
11469     // private
11470     handleResponse : function(response){
11471         this.transId = false;
11472         var options = response.argument.options;
11473         response.argument = options ? options.argument : null;
11474         this.fireEvent("requestcomplete", this, response, options);
11475         Roo.callback(options.success, options.scope, [response, options]);
11476         Roo.callback(options.callback, options.scope, [options, true, response]);
11477     },
11478
11479     // private
11480     handleFailure : function(response, e){
11481         this.transId = false;
11482         var options = response.argument.options;
11483         response.argument = options ? options.argument : null;
11484         this.fireEvent("requestexception", this, response, options, e);
11485         Roo.callback(options.failure, options.scope, [response, options]);
11486         Roo.callback(options.callback, options.scope, [options, false, response]);
11487     },
11488
11489     // private
11490     doFormUpload : function(o, ps, url){
11491         var id = Roo.id();
11492         var frame = document.createElement('iframe');
11493         frame.id = id;
11494         frame.name = id;
11495         frame.className = 'x-hidden';
11496         if(Roo.isIE){
11497             frame.src = Roo.SSL_SECURE_URL;
11498         }
11499         document.body.appendChild(frame);
11500
11501         if(Roo.isIE){
11502            document.frames[id].name = id;
11503         }
11504
11505         var form = Roo.getDom(o.form);
11506         form.target = id;
11507         form.method = 'POST';
11508         form.enctype = form.encoding = 'multipart/form-data';
11509         if(url){
11510             form.action = url;
11511         }
11512
11513         var hiddens, hd;
11514         if(ps){ // add dynamic params
11515             hiddens = [];
11516             ps = Roo.urlDecode(ps, false);
11517             for(var k in ps){
11518                 if(ps.hasOwnProperty(k)){
11519                     hd = document.createElement('input');
11520                     hd.type = 'hidden';
11521                     hd.name = k;
11522                     hd.value = ps[k];
11523                     form.appendChild(hd);
11524                     hiddens.push(hd);
11525                 }
11526             }
11527         }
11528
11529         function cb(){
11530             var r = {  // bogus response object
11531                 responseText : '',
11532                 responseXML : null
11533             };
11534
11535             r.argument = o ? o.argument : null;
11536
11537             try { //
11538                 var doc;
11539                 if(Roo.isIE){
11540                     doc = frame.contentWindow.document;
11541                 }else {
11542                     doc = (frame.contentDocument || window.frames[id].document);
11543                 }
11544                 if(doc && doc.body){
11545                     r.responseText = doc.body.innerHTML;
11546                 }
11547                 if(doc && doc.XMLDocument){
11548                     r.responseXML = doc.XMLDocument;
11549                 }else {
11550                     r.responseXML = doc;
11551                 }
11552             }
11553             catch(e) {
11554                 // ignore
11555             }
11556
11557             Roo.EventManager.removeListener(frame, 'load', cb, this);
11558
11559             this.fireEvent("requestcomplete", this, r, o);
11560             Roo.callback(o.success, o.scope, [r, o]);
11561             Roo.callback(o.callback, o.scope, [o, true, r]);
11562
11563             setTimeout(function(){document.body.removeChild(frame);}, 100);
11564         }
11565
11566         Roo.EventManager.on(frame, 'load', cb, this);
11567         form.submit();
11568
11569         if(hiddens){ // remove dynamic params
11570             for(var i = 0, len = hiddens.length; i < len; i++){
11571                 form.removeChild(hiddens[i]);
11572             }
11573         }
11574     }
11575 });
11576 /*
11577  * Based on:
11578  * Ext JS Library 1.1.1
11579  * Copyright(c) 2006-2007, Ext JS, LLC.
11580  *
11581  * Originally Released Under LGPL - original licence link has changed is not relivant.
11582  *
11583  * Fork - LGPL
11584  * <script type="text/javascript">
11585  */
11586  
11587 /**
11588  * Global Ajax request class.
11589  * 
11590  * @class Roo.Ajax
11591  * @extends Roo.data.Connection
11592  * @static
11593  * 
11594  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11595  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11596  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11597  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11598  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11599  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11600  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11601  */
11602 Roo.Ajax = new Roo.data.Connection({
11603     // fix up the docs
11604     /**
11605      * @scope Roo.Ajax
11606      * @type {Boolear} 
11607      */
11608     autoAbort : false,
11609
11610     /**
11611      * Serialize the passed form into a url encoded string
11612      * @scope Roo.Ajax
11613      * @param {String/HTMLElement} form
11614      * @return {String}
11615      */
11616     serializeForm : function(form){
11617         return Roo.lib.Ajax.serializeForm(form);
11618     }
11619 });/*
11620  * Based on:
11621  * Ext JS Library 1.1.1
11622  * Copyright(c) 2006-2007, Ext JS, LLC.
11623  *
11624  * Originally Released Under LGPL - original licence link has changed is not relivant.
11625  *
11626  * Fork - LGPL
11627  * <script type="text/javascript">
11628  */
11629
11630  
11631 /**
11632  * @class Roo.UpdateManager
11633  * @extends Roo.util.Observable
11634  * Provides AJAX-style update for Element object.<br><br>
11635  * Usage:<br>
11636  * <pre><code>
11637  * // Get it from a Roo.Element object
11638  * var el = Roo.get("foo");
11639  * var mgr = el.getUpdateManager();
11640  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11641  * ...
11642  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11643  * <br>
11644  * // or directly (returns the same UpdateManager instance)
11645  * var mgr = new Roo.UpdateManager("myElementId");
11646  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11647  * mgr.on("update", myFcnNeedsToKnow);
11648  * <br>
11649    // short handed call directly from the element object
11650    Roo.get("foo").load({
11651         url: "bar.php",
11652         scripts:true,
11653         params: "for=bar",
11654         text: "Loading Foo..."
11655    });
11656  * </code></pre>
11657  * @constructor
11658  * Create new UpdateManager directly.
11659  * @param {String/HTMLElement/Roo.Element} el The element to update
11660  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11661  */
11662 Roo.UpdateManager = function(el, forceNew){
11663     el = Roo.get(el);
11664     if(!forceNew && el.updateManager){
11665         return el.updateManager;
11666     }
11667     /**
11668      * The Element object
11669      * @type Roo.Element
11670      */
11671     this.el = el;
11672     /**
11673      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11674      * @type String
11675      */
11676     this.defaultUrl = null;
11677
11678     this.addEvents({
11679         /**
11680          * @event beforeupdate
11681          * Fired before an update is made, return false from your handler and the update is cancelled.
11682          * @param {Roo.Element} el
11683          * @param {String/Object/Function} url
11684          * @param {String/Object} params
11685          */
11686         "beforeupdate": true,
11687         /**
11688          * @event update
11689          * Fired after successful update is made.
11690          * @param {Roo.Element} el
11691          * @param {Object} oResponseObject The response Object
11692          */
11693         "update": true,
11694         /**
11695          * @event failure
11696          * Fired on update failure.
11697          * @param {Roo.Element} el
11698          * @param {Object} oResponseObject The response Object
11699          */
11700         "failure": true
11701     });
11702     var d = Roo.UpdateManager.defaults;
11703     /**
11704      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11705      * @type String
11706      */
11707     this.sslBlankUrl = d.sslBlankUrl;
11708     /**
11709      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11710      * @type Boolean
11711      */
11712     this.disableCaching = d.disableCaching;
11713     /**
11714      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11715      * @type String
11716      */
11717     this.indicatorText = d.indicatorText;
11718     /**
11719      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11720      * @type String
11721      */
11722     this.showLoadIndicator = d.showLoadIndicator;
11723     /**
11724      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11725      * @type Number
11726      */
11727     this.timeout = d.timeout;
11728
11729     /**
11730      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11731      * @type Boolean
11732      */
11733     this.loadScripts = d.loadScripts;
11734
11735     /**
11736      * Transaction object of current executing transaction
11737      */
11738     this.transaction = null;
11739
11740     /**
11741      * @private
11742      */
11743     this.autoRefreshProcId = null;
11744     /**
11745      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.refreshDelegate = this.refresh.createDelegate(this);
11749     /**
11750      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11751      * @type Function
11752      */
11753     this.updateDelegate = this.update.createDelegate(this);
11754     /**
11755      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11756      * @type Function
11757      */
11758     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11759     /**
11760      * @private
11761      */
11762     this.successDelegate = this.processSuccess.createDelegate(this);
11763     /**
11764      * @private
11765      */
11766     this.failureDelegate = this.processFailure.createDelegate(this);
11767
11768     if(!this.renderer){
11769      /**
11770       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11771       */
11772     this.renderer = new Roo.UpdateManager.BasicRenderer();
11773     }
11774     
11775     Roo.UpdateManager.superclass.constructor.call(this);
11776 };
11777
11778 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11779     /**
11780      * Get the Element this UpdateManager is bound to
11781      * @return {Roo.Element} The element
11782      */
11783     getEl : function(){
11784         return this.el;
11785     },
11786     /**
11787      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11788      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11789 <pre><code>
11790 um.update({<br/>
11791     url: "your-url.php",<br/>
11792     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11793     callback: yourFunction,<br/>
11794     scope: yourObject, //(optional scope)  <br/>
11795     discardUrl: false, <br/>
11796     nocache: false,<br/>
11797     text: "Loading...",<br/>
11798     timeout: 30,<br/>
11799     scripts: false<br/>
11800 });
11801 </code></pre>
11802      * The only required property is url. The optional properties nocache, text and scripts
11803      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11804      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11805      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11806      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11807      */
11808     update : function(url, params, callback, discardUrl){
11809         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11810             var method = this.method,
11811                 cfg;
11812             if(typeof url == "object"){ // must be config object
11813                 cfg = url;
11814                 url = cfg.url;
11815                 params = params || cfg.params;
11816                 callback = callback || cfg.callback;
11817                 discardUrl = discardUrl || cfg.discardUrl;
11818                 if(callback && cfg.scope){
11819                     callback = callback.createDelegate(cfg.scope);
11820                 }
11821                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11822                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11823                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11824                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11825                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11826             }
11827             this.showLoading();
11828             if(!discardUrl){
11829                 this.defaultUrl = url;
11830             }
11831             if(typeof url == "function"){
11832                 url = url.call(this);
11833             }
11834
11835             method = method || (params ? "POST" : "GET");
11836             if(method == "GET"){
11837                 url = this.prepareUrl(url);
11838             }
11839
11840             var o = Roo.apply(cfg ||{}, {
11841                 url : url,
11842                 params: params,
11843                 success: this.successDelegate,
11844                 failure: this.failureDelegate,
11845                 callback: undefined,
11846                 timeout: (this.timeout*1000),
11847                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11848             });
11849             Roo.log("updated manager called with timeout of " + o.timeout);
11850             this.transaction = Roo.Ajax.request(o);
11851         }
11852     },
11853
11854     /**
11855      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11856      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11857      * @param {String/HTMLElement} form The form Id or form element
11858      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11859      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11860      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11861      */
11862     formUpdate : function(form, url, reset, callback){
11863         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11864             if(typeof url == "function"){
11865                 url = url.call(this);
11866             }
11867             form = Roo.getDom(form);
11868             this.transaction = Roo.Ajax.request({
11869                 form: form,
11870                 url:url,
11871                 success: this.successDelegate,
11872                 failure: this.failureDelegate,
11873                 timeout: (this.timeout*1000),
11874                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11875             });
11876             this.showLoading.defer(1, this);
11877         }
11878     },
11879
11880     /**
11881      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11882      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11883      */
11884     refresh : function(callback){
11885         if(this.defaultUrl == null){
11886             return;
11887         }
11888         this.update(this.defaultUrl, null, callback, true);
11889     },
11890
11891     /**
11892      * Set this element to auto refresh.
11893      * @param {Number} interval How often to update (in seconds).
11894      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11895      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11896      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11897      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11898      */
11899     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11900         if(refreshNow){
11901             this.update(url || this.defaultUrl, params, callback, true);
11902         }
11903         if(this.autoRefreshProcId){
11904             clearInterval(this.autoRefreshProcId);
11905         }
11906         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11907     },
11908
11909     /**
11910      * Stop auto refresh on this element.
11911      */
11912      stopAutoRefresh : function(){
11913         if(this.autoRefreshProcId){
11914             clearInterval(this.autoRefreshProcId);
11915             delete this.autoRefreshProcId;
11916         }
11917     },
11918
11919     isAutoRefreshing : function(){
11920        return this.autoRefreshProcId ? true : false;
11921     },
11922     /**
11923      * Called to update the element to "Loading" state. Override to perform custom action.
11924      */
11925     showLoading : function(){
11926         if(this.showLoadIndicator){
11927             this.el.update(this.indicatorText);
11928         }
11929     },
11930
11931     /**
11932      * Adds unique parameter to query string if disableCaching = true
11933      * @private
11934      */
11935     prepareUrl : function(url){
11936         if(this.disableCaching){
11937             var append = "_dc=" + (new Date().getTime());
11938             if(url.indexOf("?") !== -1){
11939                 url += "&" + append;
11940             }else{
11941                 url += "?" + append;
11942             }
11943         }
11944         return url;
11945     },
11946
11947     /**
11948      * @private
11949      */
11950     processSuccess : function(response){
11951         this.transaction = null;
11952         if(response.argument.form && response.argument.reset){
11953             try{ // put in try/catch since some older FF releases had problems with this
11954                 response.argument.form.reset();
11955             }catch(e){}
11956         }
11957         if(this.loadScripts){
11958             this.renderer.render(this.el, response, this,
11959                 this.updateComplete.createDelegate(this, [response]));
11960         }else{
11961             this.renderer.render(this.el, response, this);
11962             this.updateComplete(response);
11963         }
11964     },
11965
11966     updateComplete : function(response){
11967         this.fireEvent("update", this.el, response);
11968         if(typeof response.argument.callback == "function"){
11969             response.argument.callback(this.el, true, response);
11970         }
11971     },
11972
11973     /**
11974      * @private
11975      */
11976     processFailure : function(response){
11977         this.transaction = null;
11978         this.fireEvent("failure", this.el, response);
11979         if(typeof response.argument.callback == "function"){
11980             response.argument.callback(this.el, false, response);
11981         }
11982     },
11983
11984     /**
11985      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11986      * @param {Object} renderer The object implementing the render() method
11987      */
11988     setRenderer : function(renderer){
11989         this.renderer = renderer;
11990     },
11991
11992     getRenderer : function(){
11993        return this.renderer;
11994     },
11995
11996     /**
11997      * Set the defaultUrl used for updates
11998      * @param {String/Function} defaultUrl The url or a function to call to get the url
11999      */
12000     setDefaultUrl : function(defaultUrl){
12001         this.defaultUrl = defaultUrl;
12002     },
12003
12004     /**
12005      * Aborts the executing transaction
12006      */
12007     abort : function(){
12008         if(this.transaction){
12009             Roo.Ajax.abort(this.transaction);
12010         }
12011     },
12012
12013     /**
12014      * Returns true if an update is in progress
12015      * @return {Boolean}
12016      */
12017     isUpdating : function(){
12018         if(this.transaction){
12019             return Roo.Ajax.isLoading(this.transaction);
12020         }
12021         return false;
12022     }
12023 });
12024
12025 /**
12026  * @class Roo.UpdateManager.defaults
12027  * @static (not really - but it helps the doc tool)
12028  * The defaults collection enables customizing the default properties of UpdateManager
12029  */
12030    Roo.UpdateManager.defaults = {
12031        /**
12032          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12033          * @type Number
12034          */
12035          timeout : 30,
12036
12037          /**
12038          * True to process scripts by default (Defaults to false).
12039          * @type Boolean
12040          */
12041         loadScripts : false,
12042
12043         /**
12044         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12045         * @type String
12046         */
12047         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12048         /**
12049          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12050          * @type Boolean
12051          */
12052         disableCaching : false,
12053         /**
12054          * Whether to show indicatorText when loading (Defaults to true).
12055          * @type Boolean
12056          */
12057         showLoadIndicator : true,
12058         /**
12059          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12060          * @type String
12061          */
12062         indicatorText : '<div class="loading-indicator">Loading...</div>'
12063    };
12064
12065 /**
12066  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12067  *Usage:
12068  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12069  * @param {String/HTMLElement/Roo.Element} el The element to update
12070  * @param {String} url The url
12071  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12072  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12073  * @static
12074  * @deprecated
12075  * @member Roo.UpdateManager
12076  */
12077 Roo.UpdateManager.updateElement = function(el, url, params, options){
12078     var um = Roo.get(el, true).getUpdateManager();
12079     Roo.apply(um, options);
12080     um.update(url, params, options ? options.callback : null);
12081 };
12082 // alias for backwards compat
12083 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12084 /**
12085  * @class Roo.UpdateManager.BasicRenderer
12086  * Default Content renderer. Updates the elements innerHTML with the responseText.
12087  */
12088 Roo.UpdateManager.BasicRenderer = function(){};
12089
12090 Roo.UpdateManager.BasicRenderer.prototype = {
12091     /**
12092      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12093      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12094      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12095      * @param {Roo.Element} el The element being rendered
12096      * @param {Object} response The YUI Connect response object
12097      * @param {UpdateManager} updateManager The calling update manager
12098      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12099      */
12100      render : function(el, response, updateManager, callback){
12101         el.update(response.responseText, updateManager.loadScripts, callback);
12102     }
12103 };
12104 /*
12105  * Based on:
12106  * Roo JS
12107  * (c)) Alan Knowles
12108  * Licence : LGPL
12109  */
12110
12111
12112 /**
12113  * @class Roo.DomTemplate
12114  * @extends Roo.Template
12115  * An effort at a dom based template engine..
12116  *
12117  * Similar to XTemplate, except it uses dom parsing to create the template..
12118  *
12119  * Supported features:
12120  *
12121  *  Tags:
12122
12123 <pre><code>
12124       {a_variable} - output encoded.
12125       {a_variable.format:("Y-m-d")} - call a method on the variable
12126       {a_variable:raw} - unencoded output
12127       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12128       {a_variable:this.method_on_template(...)} - call a method on the template object.
12129  
12130 </code></pre>
12131  *  The tpl tag:
12132 <pre><code>
12133         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12134         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12135         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12136         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12137   
12138 </code></pre>
12139  *      
12140  */
12141 Roo.DomTemplate = function()
12142 {
12143      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12144      if (this.html) {
12145         this.compile();
12146      }
12147 };
12148
12149
12150 Roo.extend(Roo.DomTemplate, Roo.Template, {
12151     /**
12152      * id counter for sub templates.
12153      */
12154     id : 0,
12155     /**
12156      * flag to indicate if dom parser is inside a pre,
12157      * it will strip whitespace if not.
12158      */
12159     inPre : false,
12160     
12161     /**
12162      * The various sub templates
12163      */
12164     tpls : false,
12165     
12166     
12167     
12168     /**
12169      *
12170      * basic tag replacing syntax
12171      * WORD:WORD()
12172      *
12173      * // you can fake an object call by doing this
12174      *  x.t:(test,tesT) 
12175      * 
12176      */
12177     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12178     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12179     
12180     iterChild : function (node, method) {
12181         
12182         var oldPre = this.inPre;
12183         if (node.tagName == 'PRE') {
12184             this.inPre = true;
12185         }
12186         for( var i = 0; i < node.childNodes.length; i++) {
12187             method.call(this, node.childNodes[i]);
12188         }
12189         this.inPre = oldPre;
12190     },
12191     
12192     
12193     
12194     /**
12195      * compile the template
12196      *
12197      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12198      *
12199      */
12200     compile: function()
12201     {
12202         var s = this.html;
12203         
12204         // covert the html into DOM...
12205         var doc = false;
12206         var div =false;
12207         try {
12208             doc = document.implementation.createHTMLDocument("");
12209             doc.documentElement.innerHTML =   this.html  ;
12210             div = doc.documentElement;
12211         } catch (e) {
12212             // old IE... - nasty -- it causes all sorts of issues.. with
12213             // images getting pulled from server..
12214             div = document.createElement('div');
12215             div.innerHTML = this.html;
12216         }
12217         //doc.documentElement.innerHTML = htmlBody
12218          
12219         
12220         
12221         this.tpls = [];
12222         var _t = this;
12223         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12224         
12225         var tpls = this.tpls;
12226         
12227         // create a top level template from the snippet..
12228         
12229         //Roo.log(div.innerHTML);
12230         
12231         var tpl = {
12232             uid : 'master',
12233             id : this.id++,
12234             attr : false,
12235             value : false,
12236             body : div.innerHTML,
12237             
12238             forCall : false,
12239             execCall : false,
12240             dom : div,
12241             isTop : true
12242             
12243         };
12244         tpls.unshift(tpl);
12245         
12246         
12247         // compile them...
12248         this.tpls = [];
12249         Roo.each(tpls, function(tp){
12250             this.compileTpl(tp);
12251             this.tpls[tp.id] = tp;
12252         }, this);
12253         
12254         this.master = tpls[0];
12255         return this;
12256         
12257         
12258     },
12259     
12260     compileNode : function(node, istop) {
12261         // test for
12262         //Roo.log(node);
12263         
12264         
12265         // skip anything not a tag..
12266         if (node.nodeType != 1) {
12267             if (node.nodeType == 3 && !this.inPre) {
12268                 // reduce white space..
12269                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12270                 
12271             }
12272             return;
12273         }
12274         
12275         var tpl = {
12276             uid : false,
12277             id : false,
12278             attr : false,
12279             value : false,
12280             body : '',
12281             
12282             forCall : false,
12283             execCall : false,
12284             dom : false,
12285             isTop : istop
12286             
12287             
12288         };
12289         
12290         
12291         switch(true) {
12292             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12293             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12294             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12295             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12296             // no default..
12297         }
12298         
12299         
12300         if (!tpl.attr) {
12301             // just itterate children..
12302             this.iterChild(node,this.compileNode);
12303             return;
12304         }
12305         tpl.uid = this.id++;
12306         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12307         node.removeAttribute('roo-'+ tpl.attr);
12308         if (tpl.attr != 'name') {
12309             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12310             node.parentNode.replaceChild(placeholder,  node);
12311         } else {
12312             
12313             var placeholder =  document.createElement('span');
12314             placeholder.className = 'roo-tpl-' + tpl.value;
12315             node.parentNode.replaceChild(placeholder,  node);
12316         }
12317         
12318         // parent now sees '{domtplXXXX}
12319         this.iterChild(node,this.compileNode);
12320         
12321         // we should now have node body...
12322         var div = document.createElement('div');
12323         div.appendChild(node);
12324         tpl.dom = node;
12325         // this has the unfortunate side effect of converting tagged attributes
12326         // eg. href="{...}" into %7C...%7D
12327         // this has been fixed by searching for those combo's although it's a bit hacky..
12328         
12329         
12330         tpl.body = div.innerHTML;
12331         
12332         
12333          
12334         tpl.id = tpl.uid;
12335         switch(tpl.attr) {
12336             case 'for' :
12337                 switch (tpl.value) {
12338                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12339                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12340                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12341                 }
12342                 break;
12343             
12344             case 'exec':
12345                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12346                 break;
12347             
12348             case 'if':     
12349                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12350                 break;
12351             
12352             case 'name':
12353                 tpl.id  = tpl.value; // replace non characters???
12354                 break;
12355             
12356         }
12357         
12358         
12359         this.tpls.push(tpl);
12360         
12361         
12362         
12363     },
12364     
12365     
12366     
12367     
12368     /**
12369      * Compile a segment of the template into a 'sub-template'
12370      *
12371      * 
12372      * 
12373      *
12374      */
12375     compileTpl : function(tpl)
12376     {
12377         var fm = Roo.util.Format;
12378         var useF = this.disableFormats !== true;
12379         
12380         var sep = Roo.isGecko ? "+\n" : ",\n";
12381         
12382         var undef = function(str) {
12383             Roo.debug && Roo.log("Property not found :"  + str);
12384             return '';
12385         };
12386           
12387         //Roo.log(tpl.body);
12388         
12389         
12390         
12391         var fn = function(m, lbrace, name, format, args)
12392         {
12393             //Roo.log("ARGS");
12394             //Roo.log(arguments);
12395             args = args ? args.replace(/\\'/g,"'") : args;
12396             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12397             if (typeof(format) == 'undefined') {
12398                 format =  'htmlEncode'; 
12399             }
12400             if (format == 'raw' ) {
12401                 format = false;
12402             }
12403             
12404             if(name.substr(0, 6) == 'domtpl'){
12405                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12406             }
12407             
12408             // build an array of options to determine if value is undefined..
12409             
12410             // basically get 'xxxx.yyyy' then do
12411             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12412             //    (function () { Roo.log("Property not found"); return ''; })() :
12413             //    ......
12414             
12415             var udef_ar = [];
12416             var lookfor = '';
12417             Roo.each(name.split('.'), function(st) {
12418                 lookfor += (lookfor.length ? '.': '') + st;
12419                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12420             });
12421             
12422             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12423             
12424             
12425             if(format && useF){
12426                 
12427                 args = args ? ',' + args : "";
12428                  
12429                 if(format.substr(0, 5) != "this."){
12430                     format = "fm." + format + '(';
12431                 }else{
12432                     format = 'this.call("'+ format.substr(5) + '", ';
12433                     args = ", values";
12434                 }
12435                 
12436                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12437             }
12438              
12439             if (args && args.length) {
12440                 // called with xxyx.yuu:(test,test)
12441                 // change to ()
12442                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12443             }
12444             // raw.. - :raw modifier..
12445             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12446             
12447         };
12448         var body;
12449         // branched to use + in gecko and [].join() in others
12450         if(Roo.isGecko){
12451             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12452                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12453                     "';};};";
12454         }else{
12455             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12456             body.push(tpl.body.replace(/(\r\n|\n)/g,
12457                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12458             body.push("'].join('');};};");
12459             body = body.join('');
12460         }
12461         
12462         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12463        
12464         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12465         eval(body);
12466         
12467         return this;
12468     },
12469      
12470     /**
12471      * same as applyTemplate, except it's done to one of the subTemplates
12472      * when using named templates, you can do:
12473      *
12474      * var str = pl.applySubTemplate('your-name', values);
12475      *
12476      * 
12477      * @param {Number} id of the template
12478      * @param {Object} values to apply to template
12479      * @param {Object} parent (normaly the instance of this object)
12480      */
12481     applySubTemplate : function(id, values, parent)
12482     {
12483         
12484         
12485         var t = this.tpls[id];
12486         
12487         
12488         try { 
12489             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12490                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12491                 return '';
12492             }
12493         } catch(e) {
12494             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12495             Roo.log(values);
12496           
12497             return '';
12498         }
12499         try { 
12500             
12501             if(t.execCall && t.execCall.call(this, values, parent)){
12502                 return '';
12503             }
12504         } catch(e) {
12505             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12506             Roo.log(values);
12507             return '';
12508         }
12509         
12510         try {
12511             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12512             parent = t.target ? values : parent;
12513             if(t.forCall && vs instanceof Array){
12514                 var buf = [];
12515                 for(var i = 0, len = vs.length; i < len; i++){
12516                     try {
12517                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12518                     } catch (e) {
12519                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12520                         Roo.log(e.body);
12521                         //Roo.log(t.compiled);
12522                         Roo.log(vs[i]);
12523                     }   
12524                 }
12525                 return buf.join('');
12526             }
12527         } catch (e) {
12528             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12529             Roo.log(values);
12530             return '';
12531         }
12532         try {
12533             return t.compiled.call(this, vs, parent);
12534         } catch (e) {
12535             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12536             Roo.log(e.body);
12537             //Roo.log(t.compiled);
12538             Roo.log(values);
12539             return '';
12540         }
12541     },
12542
12543    
12544
12545     applyTemplate : function(values){
12546         return this.master.compiled.call(this, values, {});
12547         //var s = this.subs;
12548     },
12549
12550     apply : function(){
12551         return this.applyTemplate.apply(this, arguments);
12552     }
12553
12554  });
12555
12556 Roo.DomTemplate.from = function(el){
12557     el = Roo.getDom(el);
12558     return new Roo.Domtemplate(el.value || el.innerHTML);
12559 };/*
12560  * Based on:
12561  * Ext JS Library 1.1.1
12562  * Copyright(c) 2006-2007, Ext JS, LLC.
12563  *
12564  * Originally Released Under LGPL - original licence link has changed is not relivant.
12565  *
12566  * Fork - LGPL
12567  * <script type="text/javascript">
12568  */
12569
12570 /**
12571  * @class Roo.util.DelayedTask
12572  * Provides a convenient method of performing setTimeout where a new
12573  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12574  * You can use this class to buffer
12575  * the keypress events for a certain number of milliseconds, and perform only if they stop
12576  * for that amount of time.
12577  * @constructor The parameters to this constructor serve as defaults and are not required.
12578  * @param {Function} fn (optional) The default function to timeout
12579  * @param {Object} scope (optional) The default scope of that timeout
12580  * @param {Array} args (optional) The default Array of arguments
12581  */
12582 Roo.util.DelayedTask = function(fn, scope, args){
12583     var id = null, d, t;
12584
12585     var call = function(){
12586         var now = new Date().getTime();
12587         if(now - t >= d){
12588             clearInterval(id);
12589             id = null;
12590             fn.apply(scope, args || []);
12591         }
12592     };
12593     /**
12594      * Cancels any pending timeout and queues a new one
12595      * @param {Number} delay The milliseconds to delay
12596      * @param {Function} newFn (optional) Overrides function passed to constructor
12597      * @param {Object} newScope (optional) Overrides scope passed to constructor
12598      * @param {Array} newArgs (optional) Overrides args passed to constructor
12599      */
12600     this.delay = function(delay, newFn, newScope, newArgs){
12601         if(id && delay != d){
12602             this.cancel();
12603         }
12604         d = delay;
12605         t = new Date().getTime();
12606         fn = newFn || fn;
12607         scope = newScope || scope;
12608         args = newArgs || args;
12609         if(!id){
12610             id = setInterval(call, d);
12611         }
12612     };
12613
12614     /**
12615      * Cancel the last queued timeout
12616      */
12617     this.cancel = function(){
12618         if(id){
12619             clearInterval(id);
12620             id = null;
12621         }
12622     };
12623 };/*
12624  * Based on:
12625  * Ext JS Library 1.1.1
12626  * Copyright(c) 2006-2007, Ext JS, LLC.
12627  *
12628  * Originally Released Under LGPL - original licence link has changed is not relivant.
12629  *
12630  * Fork - LGPL
12631  * <script type="text/javascript">
12632  */
12633  
12634  
12635 Roo.util.TaskRunner = function(interval){
12636     interval = interval || 10;
12637     var tasks = [], removeQueue = [];
12638     var id = 0;
12639     var running = false;
12640
12641     var stopThread = function(){
12642         running = false;
12643         clearInterval(id);
12644         id = 0;
12645     };
12646
12647     var startThread = function(){
12648         if(!running){
12649             running = true;
12650             id = setInterval(runTasks, interval);
12651         }
12652     };
12653
12654     var removeTask = function(task){
12655         removeQueue.push(task);
12656         if(task.onStop){
12657             task.onStop();
12658         }
12659     };
12660
12661     var runTasks = function(){
12662         if(removeQueue.length > 0){
12663             for(var i = 0, len = removeQueue.length; i < len; i++){
12664                 tasks.remove(removeQueue[i]);
12665             }
12666             removeQueue = [];
12667             if(tasks.length < 1){
12668                 stopThread();
12669                 return;
12670             }
12671         }
12672         var now = new Date().getTime();
12673         for(var i = 0, len = tasks.length; i < len; ++i){
12674             var t = tasks[i];
12675             var itime = now - t.taskRunTime;
12676             if(t.interval <= itime){
12677                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12678                 t.taskRunTime = now;
12679                 if(rt === false || t.taskRunCount === t.repeat){
12680                     removeTask(t);
12681                     return;
12682                 }
12683             }
12684             if(t.duration && t.duration <= (now - t.taskStartTime)){
12685                 removeTask(t);
12686             }
12687         }
12688     };
12689
12690     /**
12691      * Queues a new task.
12692      * @param {Object} task
12693      */
12694     this.start = function(task){
12695         tasks.push(task);
12696         task.taskStartTime = new Date().getTime();
12697         task.taskRunTime = 0;
12698         task.taskRunCount = 0;
12699         startThread();
12700         return task;
12701     };
12702
12703     this.stop = function(task){
12704         removeTask(task);
12705         return task;
12706     };
12707
12708     this.stopAll = function(){
12709         stopThread();
12710         for(var i = 0, len = tasks.length; i < len; i++){
12711             if(tasks[i].onStop){
12712                 tasks[i].onStop();
12713             }
12714         }
12715         tasks = [];
12716         removeQueue = [];
12717     };
12718 };
12719
12720 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12721  * Based on:
12722  * Ext JS Library 1.1.1
12723  * Copyright(c) 2006-2007, Ext JS, LLC.
12724  *
12725  * Originally Released Under LGPL - original licence link has changed is not relivant.
12726  *
12727  * Fork - LGPL
12728  * <script type="text/javascript">
12729  */
12730
12731  
12732 /**
12733  * @class Roo.util.MixedCollection
12734  * @extends Roo.util.Observable
12735  * A Collection class that maintains both numeric indexes and keys and exposes events.
12736  * @constructor
12737  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12738  * collection (defaults to false)
12739  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12740  * and return the key value for that item.  This is used when available to look up the key on items that
12741  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12742  * equivalent to providing an implementation for the {@link #getKey} method.
12743  */
12744 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12745     this.items = [];
12746     this.map = {};
12747     this.keys = [];
12748     this.length = 0;
12749     this.addEvents({
12750         /**
12751          * @event clear
12752          * Fires when the collection is cleared.
12753          */
12754         "clear" : true,
12755         /**
12756          * @event add
12757          * Fires when an item is added to the collection.
12758          * @param {Number} index The index at which the item was added.
12759          * @param {Object} o The item added.
12760          * @param {String} key The key associated with the added item.
12761          */
12762         "add" : true,
12763         /**
12764          * @event replace
12765          * Fires when an item is replaced in the collection.
12766          * @param {String} key he key associated with the new added.
12767          * @param {Object} old The item being replaced.
12768          * @param {Object} new The new item.
12769          */
12770         "replace" : true,
12771         /**
12772          * @event remove
12773          * Fires when an item is removed from the collection.
12774          * @param {Object} o The item being removed.
12775          * @param {String} key (optional) The key associated with the removed item.
12776          */
12777         "remove" : true,
12778         "sort" : true
12779     });
12780     this.allowFunctions = allowFunctions === true;
12781     if(keyFn){
12782         this.getKey = keyFn;
12783     }
12784     Roo.util.MixedCollection.superclass.constructor.call(this);
12785 };
12786
12787 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12788     allowFunctions : false,
12789     
12790 /**
12791  * Adds an item to the collection.
12792  * @param {String} key The key to associate with the item
12793  * @param {Object} o The item to add.
12794  * @return {Object} The item added.
12795  */
12796     add : function(key, o){
12797         if(arguments.length == 1){
12798             o = arguments[0];
12799             key = this.getKey(o);
12800         }
12801         if(typeof key == "undefined" || key === null){
12802             this.length++;
12803             this.items.push(o);
12804             this.keys.push(null);
12805         }else{
12806             var old = this.map[key];
12807             if(old){
12808                 return this.replace(key, o);
12809             }
12810             this.length++;
12811             this.items.push(o);
12812             this.map[key] = o;
12813             this.keys.push(key);
12814         }
12815         this.fireEvent("add", this.length-1, o, key);
12816         return o;
12817     },
12818        
12819 /**
12820   * MixedCollection has a generic way to fetch keys if you implement getKey.
12821 <pre><code>
12822 // normal way
12823 var mc = new Roo.util.MixedCollection();
12824 mc.add(someEl.dom.id, someEl);
12825 mc.add(otherEl.dom.id, otherEl);
12826 //and so on
12827
12828 // using getKey
12829 var mc = new Roo.util.MixedCollection();
12830 mc.getKey = function(el){
12831    return el.dom.id;
12832 };
12833 mc.add(someEl);
12834 mc.add(otherEl);
12835
12836 // or via the constructor
12837 var mc = new Roo.util.MixedCollection(false, function(el){
12838    return el.dom.id;
12839 });
12840 mc.add(someEl);
12841 mc.add(otherEl);
12842 </code></pre>
12843  * @param o {Object} The item for which to find the key.
12844  * @return {Object} The key for the passed item.
12845  */
12846     getKey : function(o){
12847          return o.id; 
12848     },
12849    
12850 /**
12851  * Replaces an item in the collection.
12852  * @param {String} key The key associated with the item to replace, or the item to replace.
12853  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12854  * @return {Object}  The new item.
12855  */
12856     replace : function(key, o){
12857         if(arguments.length == 1){
12858             o = arguments[0];
12859             key = this.getKey(o);
12860         }
12861         var old = this.item(key);
12862         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12863              return this.add(key, o);
12864         }
12865         var index = this.indexOfKey(key);
12866         this.items[index] = o;
12867         this.map[key] = o;
12868         this.fireEvent("replace", key, old, o);
12869         return o;
12870     },
12871    
12872 /**
12873  * Adds all elements of an Array or an Object to the collection.
12874  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12875  * an Array of values, each of which are added to the collection.
12876  */
12877     addAll : function(objs){
12878         if(arguments.length > 1 || objs instanceof Array){
12879             var args = arguments.length > 1 ? arguments : objs;
12880             for(var i = 0, len = args.length; i < len; i++){
12881                 this.add(args[i]);
12882             }
12883         }else{
12884             for(var key in objs){
12885                 if(this.allowFunctions || typeof objs[key] != "function"){
12886                     this.add(key, objs[key]);
12887                 }
12888             }
12889         }
12890     },
12891    
12892 /**
12893  * Executes the specified function once for every item in the collection, passing each
12894  * item as the first and only parameter. returning false from the function will stop the iteration.
12895  * @param {Function} fn The function to execute for each item.
12896  * @param {Object} scope (optional) The scope in which to execute the function.
12897  */
12898     each : function(fn, scope){
12899         var items = [].concat(this.items); // each safe for removal
12900         for(var i = 0, len = items.length; i < len; i++){
12901             if(fn.call(scope || items[i], items[i], i, len) === false){
12902                 break;
12903             }
12904         }
12905     },
12906    
12907 /**
12908  * Executes the specified function once for every key in the collection, passing each
12909  * key, and its associated item as the first two parameters.
12910  * @param {Function} fn The function to execute for each item.
12911  * @param {Object} scope (optional) The scope in which to execute the function.
12912  */
12913     eachKey : function(fn, scope){
12914         for(var i = 0, len = this.keys.length; i < len; i++){
12915             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12916         }
12917     },
12918    
12919 /**
12920  * Returns the first item in the collection which elicits a true return value from the
12921  * passed selection function.
12922  * @param {Function} fn The selection function to execute for each item.
12923  * @param {Object} scope (optional) The scope in which to execute the function.
12924  * @return {Object} The first item in the collection which returned true from the selection function.
12925  */
12926     find : function(fn, scope){
12927         for(var i = 0, len = this.items.length; i < len; i++){
12928             if(fn.call(scope || window, this.items[i], this.keys[i])){
12929                 return this.items[i];
12930             }
12931         }
12932         return null;
12933     },
12934    
12935 /**
12936  * Inserts an item at the specified index in the collection.
12937  * @param {Number} index The index to insert the item at.
12938  * @param {String} key The key to associate with the new item, or the item itself.
12939  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12940  * @return {Object} The item inserted.
12941  */
12942     insert : function(index, key, o){
12943         if(arguments.length == 2){
12944             o = arguments[1];
12945             key = this.getKey(o);
12946         }
12947         if(index >= this.length){
12948             return this.add(key, o);
12949         }
12950         this.length++;
12951         this.items.splice(index, 0, o);
12952         if(typeof key != "undefined" && key != null){
12953             this.map[key] = o;
12954         }
12955         this.keys.splice(index, 0, key);
12956         this.fireEvent("add", index, o, key);
12957         return o;
12958     },
12959    
12960 /**
12961  * Removed an item from the collection.
12962  * @param {Object} o The item to remove.
12963  * @return {Object} The item removed.
12964  */
12965     remove : function(o){
12966         return this.removeAt(this.indexOf(o));
12967     },
12968    
12969 /**
12970  * Remove an item from a specified index in the collection.
12971  * @param {Number} index The index within the collection of the item to remove.
12972  */
12973     removeAt : function(index){
12974         if(index < this.length && index >= 0){
12975             this.length--;
12976             var o = this.items[index];
12977             this.items.splice(index, 1);
12978             var key = this.keys[index];
12979             if(typeof key != "undefined"){
12980                 delete this.map[key];
12981             }
12982             this.keys.splice(index, 1);
12983             this.fireEvent("remove", o, key);
12984         }
12985     },
12986    
12987 /**
12988  * Removed an item associated with the passed key fom the collection.
12989  * @param {String} key The key of the item to remove.
12990  */
12991     removeKey : function(key){
12992         return this.removeAt(this.indexOfKey(key));
12993     },
12994    
12995 /**
12996  * Returns the number of items in the collection.
12997  * @return {Number} the number of items in the collection.
12998  */
12999     getCount : function(){
13000         return this.length; 
13001     },
13002    
13003 /**
13004  * Returns index within the collection of the passed Object.
13005  * @param {Object} o The item to find the index of.
13006  * @return {Number} index of the item.
13007  */
13008     indexOf : function(o){
13009         if(!this.items.indexOf){
13010             for(var i = 0, len = this.items.length; i < len; i++){
13011                 if(this.items[i] == o) return i;
13012             }
13013             return -1;
13014         }else{
13015             return this.items.indexOf(o);
13016         }
13017     },
13018    
13019 /**
13020  * Returns index within the collection of the passed key.
13021  * @param {String} key The key to find the index of.
13022  * @return {Number} index of the key.
13023  */
13024     indexOfKey : function(key){
13025         if(!this.keys.indexOf){
13026             for(var i = 0, len = this.keys.length; i < len; i++){
13027                 if(this.keys[i] == key) return i;
13028             }
13029             return -1;
13030         }else{
13031             return this.keys.indexOf(key);
13032         }
13033     },
13034    
13035 /**
13036  * Returns the item associated with the passed key OR index. Key has priority over index.
13037  * @param {String/Number} key The key or index of the item.
13038  * @return {Object} The item associated with the passed key.
13039  */
13040     item : function(key){
13041         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13042         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13043     },
13044     
13045 /**
13046  * Returns the item at the specified index.
13047  * @param {Number} index The index of the item.
13048  * @return {Object}
13049  */
13050     itemAt : function(index){
13051         return this.items[index];
13052     },
13053     
13054 /**
13055  * Returns the item associated with the passed key.
13056  * @param {String/Number} key The key of the item.
13057  * @return {Object} The item associated with the passed key.
13058  */
13059     key : function(key){
13060         return this.map[key];
13061     },
13062    
13063 /**
13064  * Returns true if the collection contains the passed Object as an item.
13065  * @param {Object} o  The Object to look for in the collection.
13066  * @return {Boolean} True if the collection contains the Object as an item.
13067  */
13068     contains : function(o){
13069         return this.indexOf(o) != -1;
13070     },
13071    
13072 /**
13073  * Returns true if the collection contains the passed Object as a key.
13074  * @param {String} key The key to look for in the collection.
13075  * @return {Boolean} True if the collection contains the Object as a key.
13076  */
13077     containsKey : function(key){
13078         return typeof this.map[key] != "undefined";
13079     },
13080    
13081 /**
13082  * Removes all items from the collection.
13083  */
13084     clear : function(){
13085         this.length = 0;
13086         this.items = [];
13087         this.keys = [];
13088         this.map = {};
13089         this.fireEvent("clear");
13090     },
13091    
13092 /**
13093  * Returns the first item in the collection.
13094  * @return {Object} the first item in the collection..
13095  */
13096     first : function(){
13097         return this.items[0]; 
13098     },
13099    
13100 /**
13101  * Returns the last item in the collection.
13102  * @return {Object} the last item in the collection..
13103  */
13104     last : function(){
13105         return this.items[this.length-1];   
13106     },
13107     
13108     _sort : function(property, dir, fn){
13109         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13110         fn = fn || function(a, b){
13111             return a-b;
13112         };
13113         var c = [], k = this.keys, items = this.items;
13114         for(var i = 0, len = items.length; i < len; i++){
13115             c[c.length] = {key: k[i], value: items[i], index: i};
13116         }
13117         c.sort(function(a, b){
13118             var v = fn(a[property], b[property]) * dsc;
13119             if(v == 0){
13120                 v = (a.index < b.index ? -1 : 1);
13121             }
13122             return v;
13123         });
13124         for(var i = 0, len = c.length; i < len; i++){
13125             items[i] = c[i].value;
13126             k[i] = c[i].key;
13127         }
13128         this.fireEvent("sort", this);
13129     },
13130     
13131     /**
13132      * Sorts this collection with the passed comparison function
13133      * @param {String} direction (optional) "ASC" or "DESC"
13134      * @param {Function} fn (optional) comparison function
13135      */
13136     sort : function(dir, fn){
13137         this._sort("value", dir, fn);
13138     },
13139     
13140     /**
13141      * Sorts this collection by keys
13142      * @param {String} direction (optional) "ASC" or "DESC"
13143      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13144      */
13145     keySort : function(dir, fn){
13146         this._sort("key", dir, fn || function(a, b){
13147             return String(a).toUpperCase()-String(b).toUpperCase();
13148         });
13149     },
13150     
13151     /**
13152      * Returns a range of items in this collection
13153      * @param {Number} startIndex (optional) defaults to 0
13154      * @param {Number} endIndex (optional) default to the last item
13155      * @return {Array} An array of items
13156      */
13157     getRange : function(start, end){
13158         var items = this.items;
13159         if(items.length < 1){
13160             return [];
13161         }
13162         start = start || 0;
13163         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13164         var r = [];
13165         if(start <= end){
13166             for(var i = start; i <= end; i++) {
13167                     r[r.length] = items[i];
13168             }
13169         }else{
13170             for(var i = start; i >= end; i--) {
13171                     r[r.length] = items[i];
13172             }
13173         }
13174         return r;
13175     },
13176         
13177     /**
13178      * Filter the <i>objects</i> in this collection by a specific property. 
13179      * Returns a new collection that has been filtered.
13180      * @param {String} property A property on your objects
13181      * @param {String/RegExp} value Either string that the property values 
13182      * should start with or a RegExp to test against the property
13183      * @return {MixedCollection} The new filtered collection
13184      */
13185     filter : function(property, value){
13186         if(!value.exec){ // not a regex
13187             value = String(value);
13188             if(value.length == 0){
13189                 return this.clone();
13190             }
13191             value = new RegExp("^" + Roo.escapeRe(value), "i");
13192         }
13193         return this.filterBy(function(o){
13194             return o && value.test(o[property]);
13195         });
13196         },
13197     
13198     /**
13199      * Filter by a function. * Returns a new collection that has been filtered.
13200      * The passed function will be called with each 
13201      * object in the collection. If the function returns true, the value is included 
13202      * otherwise it is filtered.
13203      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13204      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13205      * @return {MixedCollection} The new filtered collection
13206      */
13207     filterBy : function(fn, scope){
13208         var r = new Roo.util.MixedCollection();
13209         r.getKey = this.getKey;
13210         var k = this.keys, it = this.items;
13211         for(var i = 0, len = it.length; i < len; i++){
13212             if(fn.call(scope||this, it[i], k[i])){
13213                                 r.add(k[i], it[i]);
13214                         }
13215         }
13216         return r;
13217     },
13218     
13219     /**
13220      * Creates a duplicate of this collection
13221      * @return {MixedCollection}
13222      */
13223     clone : function(){
13224         var r = new Roo.util.MixedCollection();
13225         var k = this.keys, it = this.items;
13226         for(var i = 0, len = it.length; i < len; i++){
13227             r.add(k[i], it[i]);
13228         }
13229         r.getKey = this.getKey;
13230         return r;
13231     }
13232 });
13233 /**
13234  * Returns the item associated with the passed key or index.
13235  * @method
13236  * @param {String/Number} key The key or index of the item.
13237  * @return {Object} The item associated with the passed key.
13238  */
13239 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13240  * Based on:
13241  * Ext JS Library 1.1.1
13242  * Copyright(c) 2006-2007, Ext JS, LLC.
13243  *
13244  * Originally Released Under LGPL - original licence link has changed is not relivant.
13245  *
13246  * Fork - LGPL
13247  * <script type="text/javascript">
13248  */
13249 /**
13250  * @class Roo.util.JSON
13251  * Modified version of Douglas Crockford"s json.js that doesn"t
13252  * mess with the Object prototype 
13253  * http://www.json.org/js.html
13254  * @singleton
13255  */
13256 Roo.util.JSON = new (function(){
13257     var useHasOwn = {}.hasOwnProperty ? true : false;
13258     
13259     // crashes Safari in some instances
13260     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13261     
13262     var pad = function(n) {
13263         return n < 10 ? "0" + n : n;
13264     };
13265     
13266     var m = {
13267         "\b": '\\b',
13268         "\t": '\\t',
13269         "\n": '\\n',
13270         "\f": '\\f',
13271         "\r": '\\r',
13272         '"' : '\\"',
13273         "\\": '\\\\'
13274     };
13275
13276     var encodeString = function(s){
13277         if (/["\\\x00-\x1f]/.test(s)) {
13278             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13279                 var c = m[b];
13280                 if(c){
13281                     return c;
13282                 }
13283                 c = b.charCodeAt();
13284                 return "\\u00" +
13285                     Math.floor(c / 16).toString(16) +
13286                     (c % 16).toString(16);
13287             }) + '"';
13288         }
13289         return '"' + s + '"';
13290     };
13291     
13292     var encodeArray = function(o){
13293         var a = ["["], b, i, l = o.length, v;
13294             for (i = 0; i < l; i += 1) {
13295                 v = o[i];
13296                 switch (typeof v) {
13297                     case "undefined":
13298                     case "function":
13299                     case "unknown":
13300                         break;
13301                     default:
13302                         if (b) {
13303                             a.push(',');
13304                         }
13305                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13306                         b = true;
13307                 }
13308             }
13309             a.push("]");
13310             return a.join("");
13311     };
13312     
13313     var encodeDate = function(o){
13314         return '"' + o.getFullYear() + "-" +
13315                 pad(o.getMonth() + 1) + "-" +
13316                 pad(o.getDate()) + "T" +
13317                 pad(o.getHours()) + ":" +
13318                 pad(o.getMinutes()) + ":" +
13319                 pad(o.getSeconds()) + '"';
13320     };
13321     
13322     /**
13323      * Encodes an Object, Array or other value
13324      * @param {Mixed} o The variable to encode
13325      * @return {String} The JSON string
13326      */
13327     this.encode = function(o)
13328     {
13329         // should this be extended to fully wrap stringify..
13330         
13331         if(typeof o == "undefined" || o === null){
13332             return "null";
13333         }else if(o instanceof Array){
13334             return encodeArray(o);
13335         }else if(o instanceof Date){
13336             return encodeDate(o);
13337         }else if(typeof o == "string"){
13338             return encodeString(o);
13339         }else if(typeof o == "number"){
13340             return isFinite(o) ? String(o) : "null";
13341         }else if(typeof o == "boolean"){
13342             return String(o);
13343         }else {
13344             var a = ["{"], b, i, v;
13345             for (i in o) {
13346                 if(!useHasOwn || o.hasOwnProperty(i)) {
13347                     v = o[i];
13348                     switch (typeof v) {
13349                     case "undefined":
13350                     case "function":
13351                     case "unknown":
13352                         break;
13353                     default:
13354                         if(b){
13355                             a.push(',');
13356                         }
13357                         a.push(this.encode(i), ":",
13358                                 v === null ? "null" : this.encode(v));
13359                         b = true;
13360                     }
13361                 }
13362             }
13363             a.push("}");
13364             return a.join("");
13365         }
13366     };
13367     
13368     /**
13369      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13370      * @param {String} json The JSON string
13371      * @return {Object} The resulting object
13372      */
13373     this.decode = function(json){
13374         
13375         return  /** eval:var:json */ eval("(" + json + ')');
13376     };
13377 })();
13378 /** 
13379  * Shorthand for {@link Roo.util.JSON#encode}
13380  * @member Roo encode 
13381  * @method */
13382 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13383 /** 
13384  * Shorthand for {@link Roo.util.JSON#decode}
13385  * @member Roo decode 
13386  * @method */
13387 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13388 /*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398  
13399 /**
13400  * @class Roo.util.Format
13401  * Reusable data formatting functions
13402  * @singleton
13403  */
13404 Roo.util.Format = function(){
13405     var trimRe = /^\s+|\s+$/g;
13406     return {
13407         /**
13408          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13409          * @param {String} value The string to truncate
13410          * @param {Number} length The maximum length to allow before truncating
13411          * @return {String} The converted text
13412          */
13413         ellipsis : function(value, len){
13414             if(value && value.length > len){
13415                 return value.substr(0, len-3)+"...";
13416             }
13417             return value;
13418         },
13419
13420         /**
13421          * Checks a reference and converts it to empty string if it is undefined
13422          * @param {Mixed} value Reference to check
13423          * @return {Mixed} Empty string if converted, otherwise the original value
13424          */
13425         undef : function(value){
13426             return typeof value != "undefined" ? value : "";
13427         },
13428
13429         /**
13430          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13431          * @param {String} value The string to encode
13432          * @return {String} The encoded text
13433          */
13434         htmlEncode : function(value){
13435             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13436         },
13437
13438         /**
13439          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13440          * @param {String} value The string to decode
13441          * @return {String} The decoded text
13442          */
13443         htmlDecode : function(value){
13444             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13445         },
13446
13447         /**
13448          * Trims any whitespace from either side of a string
13449          * @param {String} value The text to trim
13450          * @return {String} The trimmed text
13451          */
13452         trim : function(value){
13453             return String(value).replace(trimRe, "");
13454         },
13455
13456         /**
13457          * Returns a substring from within an original string
13458          * @param {String} value The original text
13459          * @param {Number} start The start index of the substring
13460          * @param {Number} length The length of the substring
13461          * @return {String} The substring
13462          */
13463         substr : function(value, start, length){
13464             return String(value).substr(start, length);
13465         },
13466
13467         /**
13468          * Converts a string to all lower case letters
13469          * @param {String} value The text to convert
13470          * @return {String} The converted text
13471          */
13472         lowercase : function(value){
13473             return String(value).toLowerCase();
13474         },
13475
13476         /**
13477          * Converts a string to all upper case letters
13478          * @param {String} value The text to convert
13479          * @return {String} The converted text
13480          */
13481         uppercase : function(value){
13482             return String(value).toUpperCase();
13483         },
13484
13485         /**
13486          * Converts the first character only of a string to upper case
13487          * @param {String} value The text to convert
13488          * @return {String} The converted text
13489          */
13490         capitalize : function(value){
13491             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13492         },
13493
13494         // private
13495         call : function(value, fn){
13496             if(arguments.length > 2){
13497                 var args = Array.prototype.slice.call(arguments, 2);
13498                 args.unshift(value);
13499                  
13500                 return /** eval:var:value */  eval(fn).apply(window, args);
13501             }else{
13502                 /** eval:var:value */
13503                 return /** eval:var:value */ eval(fn).call(window, value);
13504             }
13505         },
13506
13507        
13508         /**
13509          * safer version of Math.toFixed..??/
13510          * @param {Number/String} value The numeric value to format
13511          * @param {Number/String} value Decimal places 
13512          * @return {String} The formatted currency string
13513          */
13514         toFixed : function(v, n)
13515         {
13516             // why not use to fixed - precision is buggered???
13517             if (!n) {
13518                 return Math.round(v-0);
13519             }
13520             var fact = Math.pow(10,n+1);
13521             v = (Math.round((v-0)*fact))/fact;
13522             var z = (''+fact).substring(2);
13523             if (v == Math.floor(v)) {
13524                 return Math.floor(v) + '.' + z;
13525             }
13526             
13527             // now just padd decimals..
13528             var ps = String(v).split('.');
13529             var fd = (ps[1] + z);
13530             var r = fd.substring(0,n); 
13531             var rm = fd.substring(n); 
13532             if (rm < 5) {
13533                 return ps[0] + '.' + r;
13534             }
13535             r*=1; // turn it into a number;
13536             r++;
13537             if (String(r).length != n) {
13538                 ps[0]*=1;
13539                 ps[0]++;
13540                 r = String(r).substring(1); // chop the end off.
13541             }
13542             
13543             return ps[0] + '.' + r;
13544              
13545         },
13546         
13547         /**
13548          * Format a number as US currency
13549          * @param {Number/String} value The numeric value to format
13550          * @return {String} The formatted currency string
13551          */
13552         usMoney : function(v){
13553             return '$' + Roo.util.Format.number(v);
13554         },
13555         
13556         /**
13557          * Format a number
13558          * eventually this should probably emulate php's number_format
13559          * @param {Number/String} value The numeric value to format
13560          * @param {Number} decimals number of decimal places
13561          * @return {String} The formatted currency string
13562          */
13563         number : function(v,decimals)
13564         {
13565             // multiply and round.
13566             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13567             var mul = Math.pow(10, decimals);
13568             var zero = String(mul).substring(1);
13569             v = (Math.round((v-0)*mul))/mul;
13570             
13571             // if it's '0' number.. then
13572             
13573             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13574             v = String(v);
13575             var ps = v.split('.');
13576             var whole = ps[0];
13577             
13578             
13579             var r = /(\d+)(\d{3})/;
13580             // add comma's
13581             while (r.test(whole)) {
13582                 whole = whole.replace(r, '$1' + ',' + '$2');
13583             }
13584             
13585             
13586             var sub = ps[1] ?
13587                     // has decimals..
13588                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13589                     // does not have decimals
13590                     (decimals ? ('.' + zero) : '');
13591             
13592             
13593             return whole + sub ;
13594         },
13595         
13596         /**
13597          * Parse a value into a formatted date using the specified format pattern.
13598          * @param {Mixed} value The value to format
13599          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13600          * @return {String} The formatted date string
13601          */
13602         date : function(v, format){
13603             if(!v){
13604                 return "";
13605             }
13606             if(!(v instanceof Date)){
13607                 v = new Date(Date.parse(v));
13608             }
13609             return v.dateFormat(format || Roo.util.Format.defaults.date);
13610         },
13611
13612         /**
13613          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13614          * @param {String} format Any valid date format string
13615          * @return {Function} The date formatting function
13616          */
13617         dateRenderer : function(format){
13618             return function(v){
13619                 return Roo.util.Format.date(v, format);  
13620             };
13621         },
13622
13623         // private
13624         stripTagsRE : /<\/?[^>]+>/gi,
13625         
13626         /**
13627          * Strips all HTML tags
13628          * @param {Mixed} value The text from which to strip tags
13629          * @return {String} The stripped text
13630          */
13631         stripTags : function(v){
13632             return !v ? v : String(v).replace(this.stripTagsRE, "");
13633         }
13634     };
13635 }();
13636 Roo.util.Format.defaults = {
13637     date : 'd/M/Y'
13638 };/*
13639  * Based on:
13640  * Ext JS Library 1.1.1
13641  * Copyright(c) 2006-2007, Ext JS, LLC.
13642  *
13643  * Originally Released Under LGPL - original licence link has changed is not relivant.
13644  *
13645  * Fork - LGPL
13646  * <script type="text/javascript">
13647  */
13648
13649
13650  
13651
13652 /**
13653  * @class Roo.MasterTemplate
13654  * @extends Roo.Template
13655  * Provides a template that can have child templates. The syntax is:
13656 <pre><code>
13657 var t = new Roo.MasterTemplate(
13658         '&lt;select name="{name}"&gt;',
13659                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13660         '&lt;/select&gt;'
13661 );
13662 t.add('options', {value: 'foo', text: 'bar'});
13663 // or you can add multiple child elements in one shot
13664 t.addAll('options', [
13665     {value: 'foo', text: 'bar'},
13666     {value: 'foo2', text: 'bar2'},
13667     {value: 'foo3', text: 'bar3'}
13668 ]);
13669 // then append, applying the master template values
13670 t.append('my-form', {name: 'my-select'});
13671 </code></pre>
13672 * A name attribute for the child template is not required if you have only one child
13673 * template or you want to refer to them by index.
13674  */
13675 Roo.MasterTemplate = function(){
13676     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13677     this.originalHtml = this.html;
13678     var st = {};
13679     var m, re = this.subTemplateRe;
13680     re.lastIndex = 0;
13681     var subIndex = 0;
13682     while(m = re.exec(this.html)){
13683         var name = m[1], content = m[2];
13684         st[subIndex] = {
13685             name: name,
13686             index: subIndex,
13687             buffer: [],
13688             tpl : new Roo.Template(content)
13689         };
13690         if(name){
13691             st[name] = st[subIndex];
13692         }
13693         st[subIndex].tpl.compile();
13694         st[subIndex].tpl.call = this.call.createDelegate(this);
13695         subIndex++;
13696     }
13697     this.subCount = subIndex;
13698     this.subs = st;
13699 };
13700 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13701     /**
13702     * The regular expression used to match sub templates
13703     * @type RegExp
13704     * @property
13705     */
13706     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13707
13708     /**
13709      * Applies the passed values to a child template.
13710      * @param {String/Number} name (optional) The name or index of the child template
13711      * @param {Array/Object} values The values to be applied to the template
13712      * @return {MasterTemplate} this
13713      */
13714      add : function(name, values){
13715         if(arguments.length == 1){
13716             values = arguments[0];
13717             name = 0;
13718         }
13719         var s = this.subs[name];
13720         s.buffer[s.buffer.length] = s.tpl.apply(values);
13721         return this;
13722     },
13723
13724     /**
13725      * Applies all the passed values to a child template.
13726      * @param {String/Number} name (optional) The name or index of the child template
13727      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13728      * @param {Boolean} reset (optional) True to reset the template first
13729      * @return {MasterTemplate} this
13730      */
13731     fill : function(name, values, reset){
13732         var a = arguments;
13733         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13734             values = a[0];
13735             name = 0;
13736             reset = a[1];
13737         }
13738         if(reset){
13739             this.reset();
13740         }
13741         for(var i = 0, len = values.length; i < len; i++){
13742             this.add(name, values[i]);
13743         }
13744         return this;
13745     },
13746
13747     /**
13748      * Resets the template for reuse
13749      * @return {MasterTemplate} this
13750      */
13751      reset : function(){
13752         var s = this.subs;
13753         for(var i = 0; i < this.subCount; i++){
13754             s[i].buffer = [];
13755         }
13756         return this;
13757     },
13758
13759     applyTemplate : function(values){
13760         var s = this.subs;
13761         var replaceIndex = -1;
13762         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13763             return s[++replaceIndex].buffer.join("");
13764         });
13765         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13766     },
13767
13768     apply : function(){
13769         return this.applyTemplate.apply(this, arguments);
13770     },
13771
13772     compile : function(){return this;}
13773 });
13774
13775 /**
13776  * Alias for fill().
13777  * @method
13778  */
13779 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13780  /**
13781  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13782  * var tpl = Roo.MasterTemplate.from('element-id');
13783  * @param {String/HTMLElement} el
13784  * @param {Object} config
13785  * @static
13786  */
13787 Roo.MasterTemplate.from = function(el, config){
13788     el = Roo.getDom(el);
13789     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13790 };/*
13791  * Based on:
13792  * Ext JS Library 1.1.1
13793  * Copyright(c) 2006-2007, Ext JS, LLC.
13794  *
13795  * Originally Released Under LGPL - original licence link has changed is not relivant.
13796  *
13797  * Fork - LGPL
13798  * <script type="text/javascript">
13799  */
13800
13801  
13802 /**
13803  * @class Roo.util.CSS
13804  * Utility class for manipulating CSS rules
13805  * @singleton
13806  */
13807 Roo.util.CSS = function(){
13808         var rules = null;
13809         var doc = document;
13810
13811     var camelRe = /(-[a-z])/gi;
13812     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13813
13814    return {
13815    /**
13816     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13817     * tag and appended to the HEAD of the document.
13818     * @param {String|Object} cssText The text containing the css rules
13819     * @param {String} id An id to add to the stylesheet for later removal
13820     * @return {StyleSheet}
13821     */
13822     createStyleSheet : function(cssText, id){
13823         var ss;
13824         var head = doc.getElementsByTagName("head")[0];
13825         var nrules = doc.createElement("style");
13826         nrules.setAttribute("type", "text/css");
13827         if(id){
13828             nrules.setAttribute("id", id);
13829         }
13830         if (typeof(cssText) != 'string') {
13831             // support object maps..
13832             // not sure if this a good idea.. 
13833             // perhaps it should be merged with the general css handling
13834             // and handle js style props.
13835             var cssTextNew = [];
13836             for(var n in cssText) {
13837                 var citems = [];
13838                 for(var k in cssText[n]) {
13839                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13840                 }
13841                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13842                 
13843             }
13844             cssText = cssTextNew.join("\n");
13845             
13846         }
13847        
13848        
13849        if(Roo.isIE){
13850            head.appendChild(nrules);
13851            ss = nrules.styleSheet;
13852            ss.cssText = cssText;
13853        }else{
13854            try{
13855                 nrules.appendChild(doc.createTextNode(cssText));
13856            }catch(e){
13857                nrules.cssText = cssText; 
13858            }
13859            head.appendChild(nrules);
13860            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13861        }
13862        this.cacheStyleSheet(ss);
13863        return ss;
13864    },
13865
13866    /**
13867     * Removes a style or link tag by id
13868     * @param {String} id The id of the tag
13869     */
13870    removeStyleSheet : function(id){
13871        var existing = doc.getElementById(id);
13872        if(existing){
13873            existing.parentNode.removeChild(existing);
13874        }
13875    },
13876
13877    /**
13878     * Dynamically swaps an existing stylesheet reference for a new one
13879     * @param {String} id The id of an existing link tag to remove
13880     * @param {String} url The href of the new stylesheet to include
13881     */
13882    swapStyleSheet : function(id, url){
13883        this.removeStyleSheet(id);
13884        var ss = doc.createElement("link");
13885        ss.setAttribute("rel", "stylesheet");
13886        ss.setAttribute("type", "text/css");
13887        ss.setAttribute("id", id);
13888        ss.setAttribute("href", url);
13889        doc.getElementsByTagName("head")[0].appendChild(ss);
13890    },
13891    
13892    /**
13893     * Refresh the rule cache if you have dynamically added stylesheets
13894     * @return {Object} An object (hash) of rules indexed by selector
13895     */
13896    refreshCache : function(){
13897        return this.getRules(true);
13898    },
13899
13900    // private
13901    cacheStyleSheet : function(stylesheet){
13902        if(!rules){
13903            rules = {};
13904        }
13905        try{// try catch for cross domain access issue
13906            var ssRules = stylesheet.cssRules || stylesheet.rules;
13907            for(var j = ssRules.length-1; j >= 0; --j){
13908                rules[ssRules[j].selectorText] = ssRules[j];
13909            }
13910        }catch(e){}
13911    },
13912    
13913    /**
13914     * Gets all css rules for the document
13915     * @param {Boolean} refreshCache true to refresh the internal cache
13916     * @return {Object} An object (hash) of rules indexed by selector
13917     */
13918    getRules : function(refreshCache){
13919                 if(rules == null || refreshCache){
13920                         rules = {};
13921                         var ds = doc.styleSheets;
13922                         for(var i =0, len = ds.length; i < len; i++){
13923                             try{
13924                         this.cacheStyleSheet(ds[i]);
13925                     }catch(e){} 
13926                 }
13927                 }
13928                 return rules;
13929         },
13930         
13931         /**
13932     * Gets an an individual CSS rule by selector(s)
13933     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13934     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13935     * @return {CSSRule} The CSS rule or null if one is not found
13936     */
13937    getRule : function(selector, refreshCache){
13938                 var rs = this.getRules(refreshCache);
13939                 if(!(selector instanceof Array)){
13940                     return rs[selector];
13941                 }
13942                 for(var i = 0; i < selector.length; i++){
13943                         if(rs[selector[i]]){
13944                                 return rs[selector[i]];
13945                         }
13946                 }
13947                 return null;
13948         },
13949         
13950         
13951         /**
13952     * Updates a rule property
13953     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13954     * @param {String} property The css property
13955     * @param {String} value The new value for the property
13956     * @return {Boolean} true If a rule was found and updated
13957     */
13958    updateRule : function(selector, property, value){
13959                 if(!(selector instanceof Array)){
13960                         var rule = this.getRule(selector);
13961                         if(rule){
13962                                 rule.style[property.replace(camelRe, camelFn)] = value;
13963                                 return true;
13964                         }
13965                 }else{
13966                         for(var i = 0; i < selector.length; i++){
13967                                 if(this.updateRule(selector[i], property, value)){
13968                                         return true;
13969                                 }
13970                         }
13971                 }
13972                 return false;
13973         }
13974    };   
13975 }();/*
13976  * Based on:
13977  * Ext JS Library 1.1.1
13978  * Copyright(c) 2006-2007, Ext JS, LLC.
13979  *
13980  * Originally Released Under LGPL - original licence link has changed is not relivant.
13981  *
13982  * Fork - LGPL
13983  * <script type="text/javascript">
13984  */
13985
13986  
13987
13988 /**
13989  * @class Roo.util.ClickRepeater
13990  * @extends Roo.util.Observable
13991  * 
13992  * A wrapper class which can be applied to any element. Fires a "click" event while the
13993  * mouse is pressed. The interval between firings may be specified in the config but
13994  * defaults to 10 milliseconds.
13995  * 
13996  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13997  * 
13998  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13999  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14000  * Similar to an autorepeat key delay.
14001  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14002  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14003  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14004  *           "interval" and "delay" are ignored. "immediate" is honored.
14005  * @cfg {Boolean} preventDefault True to prevent the default click event
14006  * @cfg {Boolean} stopDefault True to stop the default click event
14007  * 
14008  * @history
14009  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14010  *     2007-02-02 jvs Renamed to ClickRepeater
14011  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14012  *
14013  *  @constructor
14014  * @param {String/HTMLElement/Element} el The element to listen on
14015  * @param {Object} config
14016  **/
14017 Roo.util.ClickRepeater = function(el, config)
14018 {
14019     this.el = Roo.get(el);
14020     this.el.unselectable();
14021
14022     Roo.apply(this, config);
14023
14024     this.addEvents({
14025     /**
14026      * @event mousedown
14027      * Fires when the mouse button is depressed.
14028      * @param {Roo.util.ClickRepeater} this
14029      */
14030         "mousedown" : true,
14031     /**
14032      * @event click
14033      * Fires on a specified interval during the time the element is pressed.
14034      * @param {Roo.util.ClickRepeater} this
14035      */
14036         "click" : true,
14037     /**
14038      * @event mouseup
14039      * Fires when the mouse key is released.
14040      * @param {Roo.util.ClickRepeater} this
14041      */
14042         "mouseup" : true
14043     });
14044
14045     this.el.on("mousedown", this.handleMouseDown, this);
14046     if(this.preventDefault || this.stopDefault){
14047         this.el.on("click", function(e){
14048             if(this.preventDefault){
14049                 e.preventDefault();
14050             }
14051             if(this.stopDefault){
14052                 e.stopEvent();
14053             }
14054         }, this);
14055     }
14056
14057     // allow inline handler
14058     if(this.handler){
14059         this.on("click", this.handler,  this.scope || this);
14060     }
14061
14062     Roo.util.ClickRepeater.superclass.constructor.call(this);
14063 };
14064
14065 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14066     interval : 20,
14067     delay: 250,
14068     preventDefault : true,
14069     stopDefault : false,
14070     timer : 0,
14071
14072     // private
14073     handleMouseDown : function(){
14074         clearTimeout(this.timer);
14075         this.el.blur();
14076         if(this.pressClass){
14077             this.el.addClass(this.pressClass);
14078         }
14079         this.mousedownTime = new Date();
14080
14081         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14082         this.el.on("mouseout", this.handleMouseOut, this);
14083
14084         this.fireEvent("mousedown", this);
14085         this.fireEvent("click", this);
14086         
14087         this.timer = this.click.defer(this.delay || this.interval, this);
14088     },
14089
14090     // private
14091     click : function(){
14092         this.fireEvent("click", this);
14093         this.timer = this.click.defer(this.getInterval(), this);
14094     },
14095
14096     // private
14097     getInterval: function(){
14098         if(!this.accelerate){
14099             return this.interval;
14100         }
14101         var pressTime = this.mousedownTime.getElapsed();
14102         if(pressTime < 500){
14103             return 400;
14104         }else if(pressTime < 1700){
14105             return 320;
14106         }else if(pressTime < 2600){
14107             return 250;
14108         }else if(pressTime < 3500){
14109             return 180;
14110         }else if(pressTime < 4400){
14111             return 140;
14112         }else if(pressTime < 5300){
14113             return 80;
14114         }else if(pressTime < 6200){
14115             return 50;
14116         }else{
14117             return 10;
14118         }
14119     },
14120
14121     // private
14122     handleMouseOut : function(){
14123         clearTimeout(this.timer);
14124         if(this.pressClass){
14125             this.el.removeClass(this.pressClass);
14126         }
14127         this.el.on("mouseover", this.handleMouseReturn, this);
14128     },
14129
14130     // private
14131     handleMouseReturn : function(){
14132         this.el.un("mouseover", this.handleMouseReturn);
14133         if(this.pressClass){
14134             this.el.addClass(this.pressClass);
14135         }
14136         this.click();
14137     },
14138
14139     // private
14140     handleMouseUp : function(){
14141         clearTimeout(this.timer);
14142         this.el.un("mouseover", this.handleMouseReturn);
14143         this.el.un("mouseout", this.handleMouseOut);
14144         Roo.get(document).un("mouseup", this.handleMouseUp);
14145         this.el.removeClass(this.pressClass);
14146         this.fireEvent("mouseup", this);
14147     }
14148 });/*
14149  * Based on:
14150  * Ext JS Library 1.1.1
14151  * Copyright(c) 2006-2007, Ext JS, LLC.
14152  *
14153  * Originally Released Under LGPL - original licence link has changed is not relivant.
14154  *
14155  * Fork - LGPL
14156  * <script type="text/javascript">
14157  */
14158
14159  
14160 /**
14161  * @class Roo.KeyNav
14162  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14163  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14164  * way to implement custom navigation schemes for any UI component.</p>
14165  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14166  * pageUp, pageDown, del, home, end.  Usage:</p>
14167  <pre><code>
14168 var nav = new Roo.KeyNav("my-element", {
14169     "left" : function(e){
14170         this.moveLeft(e.ctrlKey);
14171     },
14172     "right" : function(e){
14173         this.moveRight(e.ctrlKey);
14174     },
14175     "enter" : function(e){
14176         this.save();
14177     },
14178     scope : this
14179 });
14180 </code></pre>
14181  * @constructor
14182  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14183  * @param {Object} config The config
14184  */
14185 Roo.KeyNav = function(el, config){
14186     this.el = Roo.get(el);
14187     Roo.apply(this, config);
14188     if(!this.disabled){
14189         this.disabled = true;
14190         this.enable();
14191     }
14192 };
14193
14194 Roo.KeyNav.prototype = {
14195     /**
14196      * @cfg {Boolean} disabled
14197      * True to disable this KeyNav instance (defaults to false)
14198      */
14199     disabled : false,
14200     /**
14201      * @cfg {String} defaultEventAction
14202      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14203      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14204      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14205      */
14206     defaultEventAction: "stopEvent",
14207     /**
14208      * @cfg {Boolean} forceKeyDown
14209      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14210      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14211      * handle keydown instead of keypress.
14212      */
14213     forceKeyDown : false,
14214
14215     // private
14216     prepareEvent : function(e){
14217         var k = e.getKey();
14218         var h = this.keyToHandler[k];
14219         //if(h && this[h]){
14220         //    e.stopPropagation();
14221         //}
14222         if(Roo.isSafari && h && k >= 37 && k <= 40){
14223             e.stopEvent();
14224         }
14225     },
14226
14227     // private
14228     relay : function(e){
14229         var k = e.getKey();
14230         var h = this.keyToHandler[k];
14231         if(h && this[h]){
14232             if(this.doRelay(e, this[h], h) !== true){
14233                 e[this.defaultEventAction]();
14234             }
14235         }
14236     },
14237
14238     // private
14239     doRelay : function(e, h, hname){
14240         return h.call(this.scope || this, e);
14241     },
14242
14243     // possible handlers
14244     enter : false,
14245     left : false,
14246     right : false,
14247     up : false,
14248     down : false,
14249     tab : false,
14250     esc : false,
14251     pageUp : false,
14252     pageDown : false,
14253     del : false,
14254     home : false,
14255     end : false,
14256
14257     // quick lookup hash
14258     keyToHandler : {
14259         37 : "left",
14260         39 : "right",
14261         38 : "up",
14262         40 : "down",
14263         33 : "pageUp",
14264         34 : "pageDown",
14265         46 : "del",
14266         36 : "home",
14267         35 : "end",
14268         13 : "enter",
14269         27 : "esc",
14270         9  : "tab"
14271     },
14272
14273         /**
14274          * Enable this KeyNav
14275          */
14276         enable: function(){
14277                 if(this.disabled){
14278             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14279             // the EventObject will normalize Safari automatically
14280             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14281                 this.el.on("keydown", this.relay,  this);
14282             }else{
14283                 this.el.on("keydown", this.prepareEvent,  this);
14284                 this.el.on("keypress", this.relay,  this);
14285             }
14286                     this.disabled = false;
14287                 }
14288         },
14289
14290         /**
14291          * Disable this KeyNav
14292          */
14293         disable: function(){
14294                 if(!this.disabled){
14295                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14296                 this.el.un("keydown", this.relay);
14297             }else{
14298                 this.el.un("keydown", this.prepareEvent);
14299                 this.el.un("keypress", this.relay);
14300             }
14301                     this.disabled = true;
14302                 }
14303         }
14304 };/*
14305  * Based on:
14306  * Ext JS Library 1.1.1
14307  * Copyright(c) 2006-2007, Ext JS, LLC.
14308  *
14309  * Originally Released Under LGPL - original licence link has changed is not relivant.
14310  *
14311  * Fork - LGPL
14312  * <script type="text/javascript">
14313  */
14314
14315  
14316 /**
14317  * @class Roo.KeyMap
14318  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14319  * The constructor accepts the same config object as defined by {@link #addBinding}.
14320  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14321  * combination it will call the function with this signature (if the match is a multi-key
14322  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14323  * A KeyMap can also handle a string representation of keys.<br />
14324  * Usage:
14325  <pre><code>
14326 // map one key by key code
14327 var map = new Roo.KeyMap("my-element", {
14328     key: 13, // or Roo.EventObject.ENTER
14329     fn: myHandler,
14330     scope: myObject
14331 });
14332
14333 // map multiple keys to one action by string
14334 var map = new Roo.KeyMap("my-element", {
14335     key: "a\r\n\t",
14336     fn: myHandler,
14337     scope: myObject
14338 });
14339
14340 // map multiple keys to multiple actions by strings and array of codes
14341 var map = new Roo.KeyMap("my-element", [
14342     {
14343         key: [10,13],
14344         fn: function(){ alert("Return was pressed"); }
14345     }, {
14346         key: "abc",
14347         fn: function(){ alert('a, b or c was pressed'); }
14348     }, {
14349         key: "\t",
14350         ctrl:true,
14351         shift:true,
14352         fn: function(){ alert('Control + shift + tab was pressed.'); }
14353     }
14354 ]);
14355 </code></pre>
14356  * <b>Note: A KeyMap starts enabled</b>
14357  * @constructor
14358  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14359  * @param {Object} config The config (see {@link #addBinding})
14360  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14361  */
14362 Roo.KeyMap = function(el, config, eventName){
14363     this.el  = Roo.get(el);
14364     this.eventName = eventName || "keydown";
14365     this.bindings = [];
14366     if(config){
14367         this.addBinding(config);
14368     }
14369     this.enable();
14370 };
14371
14372 Roo.KeyMap.prototype = {
14373     /**
14374      * True to stop the event from bubbling and prevent the default browser action if the
14375      * key was handled by the KeyMap (defaults to false)
14376      * @type Boolean
14377      */
14378     stopEvent : false,
14379
14380     /**
14381      * Add a new binding to this KeyMap. The following config object properties are supported:
14382      * <pre>
14383 Property    Type             Description
14384 ----------  ---------------  ----------------------------------------------------------------------
14385 key         String/Array     A single keycode or an array of keycodes to handle
14386 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14387 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14388 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14389 fn          Function         The function to call when KeyMap finds the expected key combination
14390 scope       Object           The scope of the callback function
14391 </pre>
14392      *
14393      * Usage:
14394      * <pre><code>
14395 // Create a KeyMap
14396 var map = new Roo.KeyMap(document, {
14397     key: Roo.EventObject.ENTER,
14398     fn: handleKey,
14399     scope: this
14400 });
14401
14402 //Add a new binding to the existing KeyMap later
14403 map.addBinding({
14404     key: 'abc',
14405     shift: true,
14406     fn: handleKey,
14407     scope: this
14408 });
14409 </code></pre>
14410      * @param {Object/Array} config A single KeyMap config or an array of configs
14411      */
14412         addBinding : function(config){
14413         if(config instanceof Array){
14414             for(var i = 0, len = config.length; i < len; i++){
14415                 this.addBinding(config[i]);
14416             }
14417             return;
14418         }
14419         var keyCode = config.key,
14420             shift = config.shift, 
14421             ctrl = config.ctrl, 
14422             alt = config.alt,
14423             fn = config.fn,
14424             scope = config.scope;
14425         if(typeof keyCode == "string"){
14426             var ks = [];
14427             var keyString = keyCode.toUpperCase();
14428             for(var j = 0, len = keyString.length; j < len; j++){
14429                 ks.push(keyString.charCodeAt(j));
14430             }
14431             keyCode = ks;
14432         }
14433         var keyArray = keyCode instanceof Array;
14434         var handler = function(e){
14435             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14436                 var k = e.getKey();
14437                 if(keyArray){
14438                     for(var i = 0, len = keyCode.length; i < len; i++){
14439                         if(keyCode[i] == k){
14440                           if(this.stopEvent){
14441                               e.stopEvent();
14442                           }
14443                           fn.call(scope || window, k, e);
14444                           return;
14445                         }
14446                     }
14447                 }else{
14448                     if(k == keyCode){
14449                         if(this.stopEvent){
14450                            e.stopEvent();
14451                         }
14452                         fn.call(scope || window, k, e);
14453                     }
14454                 }
14455             }
14456         };
14457         this.bindings.push(handler);  
14458         },
14459
14460     /**
14461      * Shorthand for adding a single key listener
14462      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14463      * following options:
14464      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14465      * @param {Function} fn The function to call
14466      * @param {Object} scope (optional) The scope of the function
14467      */
14468     on : function(key, fn, scope){
14469         var keyCode, shift, ctrl, alt;
14470         if(typeof key == "object" && !(key instanceof Array)){
14471             keyCode = key.key;
14472             shift = key.shift;
14473             ctrl = key.ctrl;
14474             alt = key.alt;
14475         }else{
14476             keyCode = key;
14477         }
14478         this.addBinding({
14479             key: keyCode,
14480             shift: shift,
14481             ctrl: ctrl,
14482             alt: alt,
14483             fn: fn,
14484             scope: scope
14485         })
14486     },
14487
14488     // private
14489     handleKeyDown : function(e){
14490             if(this.enabled){ //just in case
14491             var b = this.bindings;
14492             for(var i = 0, len = b.length; i < len; i++){
14493                 b[i].call(this, e);
14494             }
14495             }
14496         },
14497         
14498         /**
14499          * Returns true if this KeyMap is enabled
14500          * @return {Boolean} 
14501          */
14502         isEnabled : function(){
14503             return this.enabled;  
14504         },
14505         
14506         /**
14507          * Enables this KeyMap
14508          */
14509         enable: function(){
14510                 if(!this.enabled){
14511                     this.el.on(this.eventName, this.handleKeyDown, this);
14512                     this.enabled = true;
14513                 }
14514         },
14515
14516         /**
14517          * Disable this KeyMap
14518          */
14519         disable: function(){
14520                 if(this.enabled){
14521                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14522                     this.enabled = false;
14523                 }
14524         }
14525 };/*
14526  * Based on:
14527  * Ext JS Library 1.1.1
14528  * Copyright(c) 2006-2007, Ext JS, LLC.
14529  *
14530  * Originally Released Under LGPL - original licence link has changed is not relivant.
14531  *
14532  * Fork - LGPL
14533  * <script type="text/javascript">
14534  */
14535
14536  
14537 /**
14538  * @class Roo.util.TextMetrics
14539  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14540  * wide, in pixels, a given block of text will be.
14541  * @singleton
14542  */
14543 Roo.util.TextMetrics = function(){
14544     var shared;
14545     return {
14546         /**
14547          * Measures the size of the specified text
14548          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14549          * that can affect the size of the rendered text
14550          * @param {String} text The text to measure
14551          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14552          * in order to accurately measure the text height
14553          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14554          */
14555         measure : function(el, text, fixedWidth){
14556             if(!shared){
14557                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14558             }
14559             shared.bind(el);
14560             shared.setFixedWidth(fixedWidth || 'auto');
14561             return shared.getSize(text);
14562         },
14563
14564         /**
14565          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14566          * the overhead of multiple calls to initialize the style properties on each measurement.
14567          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14568          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14569          * in order to accurately measure the text height
14570          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14571          */
14572         createInstance : function(el, fixedWidth){
14573             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14574         }
14575     };
14576 }();
14577
14578  
14579
14580 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14581     var ml = new Roo.Element(document.createElement('div'));
14582     document.body.appendChild(ml.dom);
14583     ml.position('absolute');
14584     ml.setLeftTop(-1000, -1000);
14585     ml.hide();
14586
14587     if(fixedWidth){
14588         ml.setWidth(fixedWidth);
14589     }
14590      
14591     var instance = {
14592         /**
14593          * Returns the size of the specified text based on the internal element's style and width properties
14594          * @memberOf Roo.util.TextMetrics.Instance#
14595          * @param {String} text The text to measure
14596          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14597          */
14598         getSize : function(text){
14599             ml.update(text);
14600             var s = ml.getSize();
14601             ml.update('');
14602             return s;
14603         },
14604
14605         /**
14606          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14607          * that can affect the size of the rendered text
14608          * @memberOf Roo.util.TextMetrics.Instance#
14609          * @param {String/HTMLElement} el The element, dom node or id
14610          */
14611         bind : function(el){
14612             ml.setStyle(
14613                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14614             );
14615         },
14616
14617         /**
14618          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14619          * to set a fixed width in order to accurately measure the text height.
14620          * @memberOf Roo.util.TextMetrics.Instance#
14621          * @param {Number} width The width to set on the element
14622          */
14623         setFixedWidth : function(width){
14624             ml.setWidth(width);
14625         },
14626
14627         /**
14628          * Returns the measured width of the specified text
14629          * @memberOf Roo.util.TextMetrics.Instance#
14630          * @param {String} text The text to measure
14631          * @return {Number} width The width in pixels
14632          */
14633         getWidth : function(text){
14634             ml.dom.style.width = 'auto';
14635             return this.getSize(text).width;
14636         },
14637
14638         /**
14639          * Returns the measured height of the specified text.  For multiline text, be sure to call
14640          * {@link #setFixedWidth} if necessary.
14641          * @memberOf Roo.util.TextMetrics.Instance#
14642          * @param {String} text The text to measure
14643          * @return {Number} height The height in pixels
14644          */
14645         getHeight : function(text){
14646             return this.getSize(text).height;
14647         }
14648     };
14649
14650     instance.bind(bindTo);
14651
14652     return instance;
14653 };
14654
14655 // backwards compat
14656 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14657  * Based on:
14658  * Ext JS Library 1.1.1
14659  * Copyright(c) 2006-2007, Ext JS, LLC.
14660  *
14661  * Originally Released Under LGPL - original licence link has changed is not relivant.
14662  *
14663  * Fork - LGPL
14664  * <script type="text/javascript">
14665  */
14666
14667 /**
14668  * @class Roo.state.Provider
14669  * Abstract base class for state provider implementations. This class provides methods
14670  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14671  * Provider interface.
14672  */
14673 Roo.state.Provider = function(){
14674     /**
14675      * @event statechange
14676      * Fires when a state change occurs.
14677      * @param {Provider} this This state provider
14678      * @param {String} key The state key which was changed
14679      * @param {String} value The encoded value for the state
14680      */
14681     this.addEvents({
14682         "statechange": true
14683     });
14684     this.state = {};
14685     Roo.state.Provider.superclass.constructor.call(this);
14686 };
14687 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14688     /**
14689      * Returns the current value for a key
14690      * @param {String} name The key name
14691      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14692      * @return {Mixed} The state data
14693      */
14694     get : function(name, defaultValue){
14695         return typeof this.state[name] == "undefined" ?
14696             defaultValue : this.state[name];
14697     },
14698     
14699     /**
14700      * Clears a value from the state
14701      * @param {String} name The key name
14702      */
14703     clear : function(name){
14704         delete this.state[name];
14705         this.fireEvent("statechange", this, name, null);
14706     },
14707     
14708     /**
14709      * Sets the value for a key
14710      * @param {String} name The key name
14711      * @param {Mixed} value The value to set
14712      */
14713     set : function(name, value){
14714         this.state[name] = value;
14715         this.fireEvent("statechange", this, name, value);
14716     },
14717     
14718     /**
14719      * Decodes a string previously encoded with {@link #encodeValue}.
14720      * @param {String} value The value to decode
14721      * @return {Mixed} The decoded value
14722      */
14723     decodeValue : function(cookie){
14724         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14725         var matches = re.exec(unescape(cookie));
14726         if(!matches || !matches[1]) return; // non state cookie
14727         var type = matches[1];
14728         var v = matches[2];
14729         switch(type){
14730             case "n":
14731                 return parseFloat(v);
14732             case "d":
14733                 return new Date(Date.parse(v));
14734             case "b":
14735                 return (v == "1");
14736             case "a":
14737                 var all = [];
14738                 var values = v.split("^");
14739                 for(var i = 0, len = values.length; i < len; i++){
14740                     all.push(this.decodeValue(values[i]));
14741                 }
14742                 return all;
14743            case "o":
14744                 var all = {};
14745                 var values = v.split("^");
14746                 for(var i = 0, len = values.length; i < len; i++){
14747                     var kv = values[i].split("=");
14748                     all[kv[0]] = this.decodeValue(kv[1]);
14749                 }
14750                 return all;
14751            default:
14752                 return v;
14753         }
14754     },
14755     
14756     /**
14757      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14758      * @param {Mixed} value The value to encode
14759      * @return {String} The encoded value
14760      */
14761     encodeValue : function(v){
14762         var enc;
14763         if(typeof v == "number"){
14764             enc = "n:" + v;
14765         }else if(typeof v == "boolean"){
14766             enc = "b:" + (v ? "1" : "0");
14767         }else if(v instanceof Date){
14768             enc = "d:" + v.toGMTString();
14769         }else if(v instanceof Array){
14770             var flat = "";
14771             for(var i = 0, len = v.length; i < len; i++){
14772                 flat += this.encodeValue(v[i]);
14773                 if(i != len-1) flat += "^";
14774             }
14775             enc = "a:" + flat;
14776         }else if(typeof v == "object"){
14777             var flat = "";
14778             for(var key in v){
14779                 if(typeof v[key] != "function"){
14780                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14781                 }
14782             }
14783             enc = "o:" + flat.substring(0, flat.length-1);
14784         }else{
14785             enc = "s:" + v;
14786         }
14787         return escape(enc);        
14788     }
14789 });
14790
14791 /*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801 /**
14802  * @class Roo.state.Manager
14803  * This is the global state manager. By default all components that are "state aware" check this class
14804  * for state information if you don't pass them a custom state provider. In order for this class
14805  * to be useful, it must be initialized with a provider when your application initializes.
14806  <pre><code>
14807 // in your initialization function
14808 init : function(){
14809    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14810    ...
14811    // supposed you have a {@link Roo.BorderLayout}
14812    var layout = new Roo.BorderLayout(...);
14813    layout.restoreState();
14814    // or a {Roo.BasicDialog}
14815    var dialog = new Roo.BasicDialog(...);
14816    dialog.restoreState();
14817  </code></pre>
14818  * @singleton
14819  */
14820 Roo.state.Manager = function(){
14821     var provider = new Roo.state.Provider();
14822     
14823     return {
14824         /**
14825          * Configures the default state provider for your application
14826          * @param {Provider} stateProvider The state provider to set
14827          */
14828         setProvider : function(stateProvider){
14829             provider = stateProvider;
14830         },
14831         
14832         /**
14833          * Returns the current value for a key
14834          * @param {String} name The key name
14835          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14836          * @return {Mixed} The state data
14837          */
14838         get : function(key, defaultValue){
14839             return provider.get(key, defaultValue);
14840         },
14841         
14842         /**
14843          * Sets the value for a key
14844          * @param {String} name The key name
14845          * @param {Mixed} value The state data
14846          */
14847          set : function(key, value){
14848             provider.set(key, value);
14849         },
14850         
14851         /**
14852          * Clears a value from the state
14853          * @param {String} name The key name
14854          */
14855         clear : function(key){
14856             provider.clear(key);
14857         },
14858         
14859         /**
14860          * Gets the currently configured state provider
14861          * @return {Provider} The state provider
14862          */
14863         getProvider : function(){
14864             return provider;
14865         }
14866     };
14867 }();
14868 /*
14869  * Based on:
14870  * Ext JS Library 1.1.1
14871  * Copyright(c) 2006-2007, Ext JS, LLC.
14872  *
14873  * Originally Released Under LGPL - original licence link has changed is not relivant.
14874  *
14875  * Fork - LGPL
14876  * <script type="text/javascript">
14877  */
14878 /**
14879  * @class Roo.state.CookieProvider
14880  * @extends Roo.state.Provider
14881  * The default Provider implementation which saves state via cookies.
14882  * <br />Usage:
14883  <pre><code>
14884    var cp = new Roo.state.CookieProvider({
14885        path: "/cgi-bin/",
14886        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14887        domain: "roojs.com"
14888    })
14889    Roo.state.Manager.setProvider(cp);
14890  </code></pre>
14891  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14892  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14893  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14894  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14895  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14896  * domain the page is running on including the 'www' like 'www.roojs.com')
14897  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14898  * @constructor
14899  * Create a new CookieProvider
14900  * @param {Object} config The configuration object
14901  */
14902 Roo.state.CookieProvider = function(config){
14903     Roo.state.CookieProvider.superclass.constructor.call(this);
14904     this.path = "/";
14905     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14906     this.domain = null;
14907     this.secure = false;
14908     Roo.apply(this, config);
14909     this.state = this.readCookies();
14910 };
14911
14912 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14913     // private
14914     set : function(name, value){
14915         if(typeof value == "undefined" || value === null){
14916             this.clear(name);
14917             return;
14918         }
14919         this.setCookie(name, value);
14920         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14921     },
14922
14923     // private
14924     clear : function(name){
14925         this.clearCookie(name);
14926         Roo.state.CookieProvider.superclass.clear.call(this, name);
14927     },
14928
14929     // private
14930     readCookies : function(){
14931         var cookies = {};
14932         var c = document.cookie + ";";
14933         var re = /\s?(.*?)=(.*?);/g;
14934         var matches;
14935         while((matches = re.exec(c)) != null){
14936             var name = matches[1];
14937             var value = matches[2];
14938             if(name && name.substring(0,3) == "ys-"){
14939                 cookies[name.substr(3)] = this.decodeValue(value);
14940             }
14941         }
14942         return cookies;
14943     },
14944
14945     // private
14946     setCookie : function(name, value){
14947         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14948            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14949            ((this.path == null) ? "" : ("; path=" + this.path)) +
14950            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14951            ((this.secure == true) ? "; secure" : "");
14952     },
14953
14954     // private
14955     clearCookie : function(name){
14956         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14957            ((this.path == null) ? "" : ("; path=" + this.path)) +
14958            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14959            ((this.secure == true) ? "; secure" : "");
14960     }
14961 });/*
14962  * Based on:
14963  * Ext JS Library 1.1.1
14964  * Copyright(c) 2006-2007, Ext JS, LLC.
14965  *
14966  * Originally Released Under LGPL - original licence link has changed is not relivant.
14967  *
14968  * Fork - LGPL
14969  * <script type="text/javascript">
14970  */
14971  
14972
14973 /**
14974  * @class Roo.ComponentMgr
14975  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14976  * @singleton
14977  */
14978 Roo.ComponentMgr = function(){
14979     var all = new Roo.util.MixedCollection();
14980
14981     return {
14982         /**
14983          * Registers a component.
14984          * @param {Roo.Component} c The component
14985          */
14986         register : function(c){
14987             all.add(c);
14988         },
14989
14990         /**
14991          * Unregisters a component.
14992          * @param {Roo.Component} c The component
14993          */
14994         unregister : function(c){
14995             all.remove(c);
14996         },
14997
14998         /**
14999          * Returns a component by id
15000          * @param {String} id The component id
15001          */
15002         get : function(id){
15003             return all.get(id);
15004         },
15005
15006         /**
15007          * Registers a function that will be called when a specified component is added to ComponentMgr
15008          * @param {String} id The component id
15009          * @param {Funtction} fn The callback function
15010          * @param {Object} scope The scope of the callback
15011          */
15012         onAvailable : function(id, fn, scope){
15013             all.on("add", function(index, o){
15014                 if(o.id == id){
15015                     fn.call(scope || o, o);
15016                     all.un("add", fn, scope);
15017                 }
15018             });
15019         }
15020     };
15021 }();/*
15022  * Based on:
15023  * Ext JS Library 1.1.1
15024  * Copyright(c) 2006-2007, Ext JS, LLC.
15025  *
15026  * Originally Released Under LGPL - original licence link has changed is not relivant.
15027  *
15028  * Fork - LGPL
15029  * <script type="text/javascript">
15030  */
15031  
15032 /**
15033  * @class Roo.Component
15034  * @extends Roo.util.Observable
15035  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15036  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15037  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15038  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15039  * All visual components (widgets) that require rendering into a layout should subclass Component.
15040  * @constructor
15041  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15042  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15043  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15044  */
15045 Roo.Component = function(config){
15046     config = config || {};
15047     if(config.tagName || config.dom || typeof config == "string"){ // element object
15048         config = {el: config, id: config.id || config};
15049     }
15050     this.initialConfig = config;
15051
15052     Roo.apply(this, config);
15053     this.addEvents({
15054         /**
15055          * @event disable
15056          * Fires after the component is disabled.
15057              * @param {Roo.Component} this
15058              */
15059         disable : true,
15060         /**
15061          * @event enable
15062          * Fires after the component is enabled.
15063              * @param {Roo.Component} this
15064              */
15065         enable : true,
15066         /**
15067          * @event beforeshow
15068          * Fires before the component is shown.  Return false to stop the show.
15069              * @param {Roo.Component} this
15070              */
15071         beforeshow : true,
15072         /**
15073          * @event show
15074          * Fires after the component is shown.
15075              * @param {Roo.Component} this
15076              */
15077         show : true,
15078         /**
15079          * @event beforehide
15080          * Fires before the component is hidden. Return false to stop the hide.
15081              * @param {Roo.Component} this
15082              */
15083         beforehide : true,
15084         /**
15085          * @event hide
15086          * Fires after the component is hidden.
15087              * @param {Roo.Component} this
15088              */
15089         hide : true,
15090         /**
15091          * @event beforerender
15092          * Fires before the component is rendered. Return false to stop the render.
15093              * @param {Roo.Component} this
15094              */
15095         beforerender : true,
15096         /**
15097          * @event render
15098          * Fires after the component is rendered.
15099              * @param {Roo.Component} this
15100              */
15101         render : true,
15102         /**
15103          * @event beforedestroy
15104          * Fires before the component is destroyed. Return false to stop the destroy.
15105              * @param {Roo.Component} this
15106              */
15107         beforedestroy : true,
15108         /**
15109          * @event destroy
15110          * Fires after the component is destroyed.
15111              * @param {Roo.Component} this
15112              */
15113         destroy : true
15114     });
15115     if(!this.id){
15116         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15117     }
15118     Roo.ComponentMgr.register(this);
15119     Roo.Component.superclass.constructor.call(this);
15120     this.initComponent();
15121     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15122         this.render(this.renderTo);
15123         delete this.renderTo;
15124     }
15125 };
15126
15127 /** @private */
15128 Roo.Component.AUTO_ID = 1000;
15129
15130 Roo.extend(Roo.Component, Roo.util.Observable, {
15131     /**
15132      * @scope Roo.Component.prototype
15133      * @type {Boolean}
15134      * true if this component is hidden. Read-only.
15135      */
15136     hidden : false,
15137     /**
15138      * @type {Boolean}
15139      * true if this component is disabled. Read-only.
15140      */
15141     disabled : false,
15142     /**
15143      * @type {Boolean}
15144      * true if this component has been rendered. Read-only.
15145      */
15146     rendered : false,
15147     
15148     /** @cfg {String} disableClass
15149      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15150      */
15151     disabledClass : "x-item-disabled",
15152         /** @cfg {Boolean} allowDomMove
15153          * Whether the component can move the Dom node when rendering (defaults to true).
15154          */
15155     allowDomMove : true,
15156     /** @cfg {String} hideMode
15157      * How this component should hidden. Supported values are
15158      * "visibility" (css visibility), "offsets" (negative offset position) and
15159      * "display" (css display) - defaults to "display".
15160      */
15161     hideMode: 'display',
15162
15163     /** @private */
15164     ctype : "Roo.Component",
15165
15166     /**
15167      * @cfg {String} actionMode 
15168      * which property holds the element that used for  hide() / show() / disable() / enable()
15169      * default is 'el' 
15170      */
15171     actionMode : "el",
15172
15173     /** @private */
15174     getActionEl : function(){
15175         return this[this.actionMode];
15176     },
15177
15178     initComponent : Roo.emptyFn,
15179     /**
15180      * If this is a lazy rendering component, render it to its container element.
15181      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15182      */
15183     render : function(container, position){
15184         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15185             if(!container && this.el){
15186                 this.el = Roo.get(this.el);
15187                 container = this.el.dom.parentNode;
15188                 this.allowDomMove = false;
15189             }
15190             this.container = Roo.get(container);
15191             this.rendered = true;
15192             if(position !== undefined){
15193                 if(typeof position == 'number'){
15194                     position = this.container.dom.childNodes[position];
15195                 }else{
15196                     position = Roo.getDom(position);
15197                 }
15198             }
15199             this.onRender(this.container, position || null);
15200             if(this.cls){
15201                 this.el.addClass(this.cls);
15202                 delete this.cls;
15203             }
15204             if(this.style){
15205                 this.el.applyStyles(this.style);
15206                 delete this.style;
15207             }
15208             this.fireEvent("render", this);
15209             this.afterRender(this.container);
15210             if(this.hidden){
15211                 this.hide();
15212             }
15213             if(this.disabled){
15214                 this.disable();
15215             }
15216         }
15217         return this;
15218     },
15219
15220     /** @private */
15221     // default function is not really useful
15222     onRender : function(ct, position){
15223         if(this.el){
15224             this.el = Roo.get(this.el);
15225             if(this.allowDomMove !== false){
15226                 ct.dom.insertBefore(this.el.dom, position);
15227             }
15228         }
15229     },
15230
15231     /** @private */
15232     getAutoCreate : function(){
15233         var cfg = typeof this.autoCreate == "object" ?
15234                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15235         if(this.id && !cfg.id){
15236             cfg.id = this.id;
15237         }
15238         return cfg;
15239     },
15240
15241     /** @private */
15242     afterRender : Roo.emptyFn,
15243
15244     /**
15245      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15246      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15247      */
15248     destroy : function(){
15249         if(this.fireEvent("beforedestroy", this) !== false){
15250             this.purgeListeners();
15251             this.beforeDestroy();
15252             if(this.rendered){
15253                 this.el.removeAllListeners();
15254                 this.el.remove();
15255                 if(this.actionMode == "container"){
15256                     this.container.remove();
15257                 }
15258             }
15259             this.onDestroy();
15260             Roo.ComponentMgr.unregister(this);
15261             this.fireEvent("destroy", this);
15262         }
15263     },
15264
15265         /** @private */
15266     beforeDestroy : function(){
15267
15268     },
15269
15270         /** @private */
15271         onDestroy : function(){
15272
15273     },
15274
15275     /**
15276      * Returns the underlying {@link Roo.Element}.
15277      * @return {Roo.Element} The element
15278      */
15279     getEl : function(){
15280         return this.el;
15281     },
15282
15283     /**
15284      * Returns the id of this component.
15285      * @return {String}
15286      */
15287     getId : function(){
15288         return this.id;
15289     },
15290
15291     /**
15292      * Try to focus this component.
15293      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15294      * @return {Roo.Component} this
15295      */
15296     focus : function(selectText){
15297         if(this.rendered){
15298             this.el.focus();
15299             if(selectText === true){
15300                 this.el.dom.select();
15301             }
15302         }
15303         return this;
15304     },
15305
15306     /** @private */
15307     blur : function(){
15308         if(this.rendered){
15309             this.el.blur();
15310         }
15311         return this;
15312     },
15313
15314     /**
15315      * Disable this component.
15316      * @return {Roo.Component} this
15317      */
15318     disable : function(){
15319         if(this.rendered){
15320             this.onDisable();
15321         }
15322         this.disabled = true;
15323         this.fireEvent("disable", this);
15324         return this;
15325     },
15326
15327         // private
15328     onDisable : function(){
15329         this.getActionEl().addClass(this.disabledClass);
15330         this.el.dom.disabled = true;
15331     },
15332
15333     /**
15334      * Enable this component.
15335      * @return {Roo.Component} this
15336      */
15337     enable : function(){
15338         if(this.rendered){
15339             this.onEnable();
15340         }
15341         this.disabled = false;
15342         this.fireEvent("enable", this);
15343         return this;
15344     },
15345
15346         // private
15347     onEnable : function(){
15348         this.getActionEl().removeClass(this.disabledClass);
15349         this.el.dom.disabled = false;
15350     },
15351
15352     /**
15353      * Convenience function for setting disabled/enabled by boolean.
15354      * @param {Boolean} disabled
15355      */
15356     setDisabled : function(disabled){
15357         this[disabled ? "disable" : "enable"]();
15358     },
15359
15360     /**
15361      * Show this component.
15362      * @return {Roo.Component} this
15363      */
15364     show: function(){
15365         if(this.fireEvent("beforeshow", this) !== false){
15366             this.hidden = false;
15367             if(this.rendered){
15368                 this.onShow();
15369             }
15370             this.fireEvent("show", this);
15371         }
15372         return this;
15373     },
15374
15375     // private
15376     onShow : function(){
15377         var ae = this.getActionEl();
15378         if(this.hideMode == 'visibility'){
15379             ae.dom.style.visibility = "visible";
15380         }else if(this.hideMode == 'offsets'){
15381             ae.removeClass('x-hidden');
15382         }else{
15383             ae.dom.style.display = "";
15384         }
15385     },
15386
15387     /**
15388      * Hide this component.
15389      * @return {Roo.Component} this
15390      */
15391     hide: function(){
15392         if(this.fireEvent("beforehide", this) !== false){
15393             this.hidden = true;
15394             if(this.rendered){
15395                 this.onHide();
15396             }
15397             this.fireEvent("hide", this);
15398         }
15399         return this;
15400     },
15401
15402     // private
15403     onHide : function(){
15404         var ae = this.getActionEl();
15405         if(this.hideMode == 'visibility'){
15406             ae.dom.style.visibility = "hidden";
15407         }else if(this.hideMode == 'offsets'){
15408             ae.addClass('x-hidden');
15409         }else{
15410             ae.dom.style.display = "none";
15411         }
15412     },
15413
15414     /**
15415      * Convenience function to hide or show this component by boolean.
15416      * @param {Boolean} visible True to show, false to hide
15417      * @return {Roo.Component} this
15418      */
15419     setVisible: function(visible){
15420         if(visible) {
15421             this.show();
15422         }else{
15423             this.hide();
15424         }
15425         return this;
15426     },
15427
15428     /**
15429      * Returns true if this component is visible.
15430      */
15431     isVisible : function(){
15432         return this.getActionEl().isVisible();
15433     },
15434
15435     cloneConfig : function(overrides){
15436         overrides = overrides || {};
15437         var id = overrides.id || Roo.id();
15438         var cfg = Roo.applyIf(overrides, this.initialConfig);
15439         cfg.id = id; // prevent dup id
15440         return new this.constructor(cfg);
15441     }
15442 });/*
15443  * Based on:
15444  * Ext JS Library 1.1.1
15445  * Copyright(c) 2006-2007, Ext JS, LLC.
15446  *
15447  * Originally Released Under LGPL - original licence link has changed is not relivant.
15448  *
15449  * Fork - LGPL
15450  * <script type="text/javascript">
15451  */
15452
15453 /**
15454  * @class Roo.BoxComponent
15455  * @extends Roo.Component
15456  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15457  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15458  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15459  * layout containers.
15460  * @constructor
15461  * @param {Roo.Element/String/Object} config The configuration options.
15462  */
15463 Roo.BoxComponent = function(config){
15464     Roo.Component.call(this, config);
15465     this.addEvents({
15466         /**
15467          * @event resize
15468          * Fires after the component is resized.
15469              * @param {Roo.Component} this
15470              * @param {Number} adjWidth The box-adjusted width that was set
15471              * @param {Number} adjHeight The box-adjusted height that was set
15472              * @param {Number} rawWidth The width that was originally specified
15473              * @param {Number} rawHeight The height that was originally specified
15474              */
15475         resize : true,
15476         /**
15477          * @event move
15478          * Fires after the component is moved.
15479              * @param {Roo.Component} this
15480              * @param {Number} x The new x position
15481              * @param {Number} y The new y position
15482              */
15483         move : true
15484     });
15485 };
15486
15487 Roo.extend(Roo.BoxComponent, Roo.Component, {
15488     // private, set in afterRender to signify that the component has been rendered
15489     boxReady : false,
15490     // private, used to defer height settings to subclasses
15491     deferHeight: false,
15492     /** @cfg {Number} width
15493      * width (optional) size of component
15494      */
15495      /** @cfg {Number} height
15496      * height (optional) size of component
15497      */
15498      
15499     /**
15500      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15501      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15502      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15503      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15504      * @return {Roo.BoxComponent} this
15505      */
15506     setSize : function(w, h){
15507         // support for standard size objects
15508         if(typeof w == 'object'){
15509             h = w.height;
15510             w = w.width;
15511         }
15512         // not rendered
15513         if(!this.boxReady){
15514             this.width = w;
15515             this.height = h;
15516             return this;
15517         }
15518
15519         // prevent recalcs when not needed
15520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15521             return this;
15522         }
15523         this.lastSize = {width: w, height: h};
15524
15525         var adj = this.adjustSize(w, h);
15526         var aw = adj.width, ah = adj.height;
15527         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15528             var rz = this.getResizeEl();
15529             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15530                 rz.setSize(aw, ah);
15531             }else if(!this.deferHeight && ah !== undefined){
15532                 rz.setHeight(ah);
15533             }else if(aw !== undefined){
15534                 rz.setWidth(aw);
15535             }
15536             this.onResize(aw, ah, w, h);
15537             this.fireEvent('resize', this, aw, ah, w, h);
15538         }
15539         return this;
15540     },
15541
15542     /**
15543      * Gets the current size of the component's underlying element.
15544      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15545      */
15546     getSize : function(){
15547         return this.el.getSize();
15548     },
15549
15550     /**
15551      * Gets the current XY position of the component's underlying element.
15552      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15553      * @return {Array} The XY position of the element (e.g., [100, 200])
15554      */
15555     getPosition : function(local){
15556         if(local === true){
15557             return [this.el.getLeft(true), this.el.getTop(true)];
15558         }
15559         return this.xy || this.el.getXY();
15560     },
15561
15562     /**
15563      * Gets the current box measurements of the component's underlying element.
15564      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15565      * @returns {Object} box An object in the format {x, y, width, height}
15566      */
15567     getBox : function(local){
15568         var s = this.el.getSize();
15569         if(local){
15570             s.x = this.el.getLeft(true);
15571             s.y = this.el.getTop(true);
15572         }else{
15573             var xy = this.xy || this.el.getXY();
15574             s.x = xy[0];
15575             s.y = xy[1];
15576         }
15577         return s;
15578     },
15579
15580     /**
15581      * Sets the current box measurements of the component's underlying element.
15582      * @param {Object} box An object in the format {x, y, width, height}
15583      * @returns {Roo.BoxComponent} this
15584      */
15585     updateBox : function(box){
15586         this.setSize(box.width, box.height);
15587         this.setPagePosition(box.x, box.y);
15588         return this;
15589     },
15590
15591     // protected
15592     getResizeEl : function(){
15593         return this.resizeEl || this.el;
15594     },
15595
15596     // protected
15597     getPositionEl : function(){
15598         return this.positionEl || this.el;
15599     },
15600
15601     /**
15602      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15603      * This method fires the move event.
15604      * @param {Number} left The new left
15605      * @param {Number} top The new top
15606      * @returns {Roo.BoxComponent} this
15607      */
15608     setPosition : function(x, y){
15609         this.x = x;
15610         this.y = y;
15611         if(!this.boxReady){
15612             return this;
15613         }
15614         var adj = this.adjustPosition(x, y);
15615         var ax = adj.x, ay = adj.y;
15616
15617         var el = this.getPositionEl();
15618         if(ax !== undefined || ay !== undefined){
15619             if(ax !== undefined && ay !== undefined){
15620                 el.setLeftTop(ax, ay);
15621             }else if(ax !== undefined){
15622                 el.setLeft(ax);
15623             }else if(ay !== undefined){
15624                 el.setTop(ay);
15625             }
15626             this.onPosition(ax, ay);
15627             this.fireEvent('move', this, ax, ay);
15628         }
15629         return this;
15630     },
15631
15632     /**
15633      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15634      * This method fires the move event.
15635      * @param {Number} x The new x position
15636      * @param {Number} y The new y position
15637      * @returns {Roo.BoxComponent} this
15638      */
15639     setPagePosition : function(x, y){
15640         this.pageX = x;
15641         this.pageY = y;
15642         if(!this.boxReady){
15643             return;
15644         }
15645         if(x === undefined || y === undefined){ // cannot translate undefined points
15646             return;
15647         }
15648         var p = this.el.translatePoints(x, y);
15649         this.setPosition(p.left, p.top);
15650         return this;
15651     },
15652
15653     // private
15654     onRender : function(ct, position){
15655         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15656         if(this.resizeEl){
15657             this.resizeEl = Roo.get(this.resizeEl);
15658         }
15659         if(this.positionEl){
15660             this.positionEl = Roo.get(this.positionEl);
15661         }
15662     },
15663
15664     // private
15665     afterRender : function(){
15666         Roo.BoxComponent.superclass.afterRender.call(this);
15667         this.boxReady = true;
15668         this.setSize(this.width, this.height);
15669         if(this.x || this.y){
15670             this.setPosition(this.x, this.y);
15671         }
15672         if(this.pageX || this.pageY){
15673             this.setPagePosition(this.pageX, this.pageY);
15674         }
15675     },
15676
15677     /**
15678      * Force the component's size to recalculate based on the underlying element's current height and width.
15679      * @returns {Roo.BoxComponent} this
15680      */
15681     syncSize : function(){
15682         delete this.lastSize;
15683         this.setSize(this.el.getWidth(), this.el.getHeight());
15684         return this;
15685     },
15686
15687     /**
15688      * Called after the component is resized, this method is empty by default but can be implemented by any
15689      * subclass that needs to perform custom logic after a resize occurs.
15690      * @param {Number} adjWidth The box-adjusted width that was set
15691      * @param {Number} adjHeight The box-adjusted height that was set
15692      * @param {Number} rawWidth The width that was originally specified
15693      * @param {Number} rawHeight The height that was originally specified
15694      */
15695     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15696
15697     },
15698
15699     /**
15700      * Called after the component is moved, this method is empty by default but can be implemented by any
15701      * subclass that needs to perform custom logic after a move occurs.
15702      * @param {Number} x The new x position
15703      * @param {Number} y The new y position
15704      */
15705     onPosition : function(x, y){
15706
15707     },
15708
15709     // private
15710     adjustSize : function(w, h){
15711         if(this.autoWidth){
15712             w = 'auto';
15713         }
15714         if(this.autoHeight){
15715             h = 'auto';
15716         }
15717         return {width : w, height: h};
15718     },
15719
15720     // private
15721     adjustPosition : function(x, y){
15722         return {x : x, y: y};
15723     }
15724 });/*
15725  * Original code for Roojs - LGPL
15726  * <script type="text/javascript">
15727  */
15728  
15729 /**
15730  * @class Roo.XComponent
15731  * A delayed Element creator...
15732  * Or a way to group chunks of interface together.
15733  * 
15734  * Mypart.xyx = new Roo.XComponent({
15735
15736     parent : 'Mypart.xyz', // empty == document.element.!!
15737     order : '001',
15738     name : 'xxxx'
15739     region : 'xxxx'
15740     disabled : function() {} 
15741      
15742     tree : function() { // return an tree of xtype declared components
15743         var MODULE = this;
15744         return 
15745         {
15746             xtype : 'NestedLayoutPanel',
15747             // technicall
15748         }
15749      ]
15750  *})
15751  *
15752  *
15753  * It can be used to build a big heiracy, with parent etc.
15754  * or you can just use this to render a single compoent to a dom element
15755  * MYPART.render(Roo.Element | String(id) | dom_element )
15756  * 
15757  * @extends Roo.util.Observable
15758  * @constructor
15759  * @param cfg {Object} configuration of component
15760  * 
15761  */
15762 Roo.XComponent = function(cfg) {
15763     Roo.apply(this, cfg);
15764     this.addEvents({ 
15765         /**
15766              * @event built
15767              * Fires when this the componnt is built
15768              * @param {Roo.XComponent} c the component
15769              */
15770         'built' : true
15771         
15772     });
15773     this.region = this.region || 'center'; // default..
15774     Roo.XComponent.register(this);
15775     this.modules = false;
15776     this.el = false; // where the layout goes..
15777     
15778     
15779 }
15780 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15781     /**
15782      * @property el
15783      * The created element (with Roo.factory())
15784      * @type {Roo.Layout}
15785      */
15786     el  : false,
15787     
15788     /**
15789      * @property el
15790      * for BC  - use el in new code
15791      * @type {Roo.Layout}
15792      */
15793     panel : false,
15794     
15795     /**
15796      * @property layout
15797      * for BC  - use el in new code
15798      * @type {Roo.Layout}
15799      */
15800     layout : false,
15801     
15802      /**
15803      * @cfg {Function|boolean} disabled
15804      * If this module is disabled by some rule, return true from the funtion
15805      */
15806     disabled : false,
15807     
15808     /**
15809      * @cfg {String} parent 
15810      * Name of parent element which it get xtype added to..
15811      */
15812     parent: false,
15813     
15814     /**
15815      * @cfg {String} order
15816      * Used to set the order in which elements are created (usefull for multiple tabs)
15817      */
15818     
15819     order : false,
15820     /**
15821      * @cfg {String} name
15822      * String to display while loading.
15823      */
15824     name : false,
15825     /**
15826      * @cfg {String} region
15827      * Region to render component to (defaults to center)
15828      */
15829     region : 'center',
15830     
15831     /**
15832      * @cfg {Array} items
15833      * A single item array - the first element is the root of the tree..
15834      * It's done this way to stay compatible with the Xtype system...
15835      */
15836     items : false,
15837     
15838     /**
15839      * @property _tree
15840      * The method that retuns the tree of parts that make up this compoennt 
15841      * @type {function}
15842      */
15843     _tree  : false,
15844     
15845      /**
15846      * render
15847      * render element to dom or tree
15848      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15849      */
15850     
15851     render : function(el)
15852     {
15853         
15854         el = el || false;
15855         var hp = this.parent ? 1 : 0;
15856         
15857         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15858             // if parent is a '#.....' string, then let's use that..
15859             var ename = this.parent.substr(1)
15860             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15861             el = Roo.get(ename);
15862             if (!el && !this.parent) {
15863                 Roo.log("Warning - element can not be found :#" + ename );
15864                 return;
15865             }
15866         }
15867         var tree = this._tree ? this._tree() : this.tree();
15868
15869         
15870         if (!this.parent && typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) {
15871             //el = Roo.get(document.body);
15872             this.parent = { el : true };
15873         }
15874             
15875             
15876         
15877         if (!this.parent) {
15878             
15879             Roo.log("no parent - creating one");
15880             
15881             el = el ? Roo.get(el) : false;      
15882             
15883             // it's a top level one..
15884             this.parent =  {
15885                 el : new Roo.BorderLayout(el || document.body, {
15886                 
15887                      center: {
15888                          titlebar: false,
15889                          autoScroll:false,
15890                          closeOnTab: true,
15891                          tabPosition: 'top',
15892                           //resizeTabs: true,
15893                          alwaysShowTabs: el && hp? false :  true,
15894                          hideTabs: el || !hp ? true :  false,
15895                          minTabWidth: 140
15896                      }
15897                  })
15898             }
15899         }
15900         
15901                 if (!this.parent.el) {
15902                         // probably an old style ctor, which has been disabled.
15903                         return;
15904                         
15905                 }
15906                 // The 'tree' method is  '_tree now' 
15907             
15908         tree.region = tree.region || this.region;
15909         
15910         if (this.parent.el === true) {
15911             // bootstrap... - body..
15912             this.parent.el = Roo.factory(tree);
15913         }
15914         
15915         this.el = this.parent.el.addxtype(tree);
15916         this.fireEvent('built', this);
15917         
15918         this.panel = this.el;
15919         this.layout = this.panel.layout;
15920                 this.parentLayout = this.parent.layout  || false;  
15921          
15922     }
15923     
15924 });
15925
15926 Roo.apply(Roo.XComponent, {
15927     /**
15928      * @property  hideProgress
15929      * true to disable the building progress bar.. usefull on single page renders.
15930      * @type Boolean
15931      */
15932     hideProgress : false,
15933     /**
15934      * @property  buildCompleted
15935      * True when the builder has completed building the interface.
15936      * @type Boolean
15937      */
15938     buildCompleted : false,
15939      
15940     /**
15941      * @property  topModule
15942      * the upper most module - uses document.element as it's constructor.
15943      * @type Object
15944      */
15945      
15946     topModule  : false,
15947       
15948     /**
15949      * @property  modules
15950      * array of modules to be created by registration system.
15951      * @type {Array} of Roo.XComponent
15952      */
15953     
15954     modules : [],
15955     /**
15956      * @property  elmodules
15957      * array of modules to be created by which use #ID 
15958      * @type {Array} of Roo.XComponent
15959      */
15960      
15961     elmodules : [],
15962
15963      /**
15964      * @property  build_from_html
15965      * Build elements from html - used by bootstrap HTML stuff 
15966      *    - this is cleared after build is completed
15967      * @type {boolean} true  (default false)
15968      */
15969      
15970     build_from_html : false,
15971
15972     /**
15973      * Register components to be built later.
15974      *
15975      * This solves the following issues
15976      * - Building is not done on page load, but after an authentication process has occured.
15977      * - Interface elements are registered on page load
15978      * - Parent Interface elements may not be loaded before child, so this handles that..
15979      * 
15980      *
15981      * example:
15982      * 
15983      * MyApp.register({
15984           order : '000001',
15985           module : 'Pman.Tab.projectMgr',
15986           region : 'center',
15987           parent : 'Pman.layout',
15988           disabled : false,  // or use a function..
15989         })
15990      
15991      * * @param {Object} details about module
15992      */
15993     register : function(obj) {
15994                 
15995         Roo.XComponent.event.fireEvent('register', obj);
15996         switch(typeof(obj.disabled) ) {
15997                 
15998             case 'undefined':
15999                 break;
16000             
16001             case 'function':
16002                 if ( obj.disabled() ) {
16003                         return;
16004                 }
16005                 break;
16006             
16007             default:
16008                 if (obj.disabled) {
16009                         return;
16010                 }
16011                 break;
16012         }
16013                 
16014         this.modules.push(obj);
16015          
16016     },
16017     /**
16018      * convert a string to an object..
16019      * eg. 'AAA.BBB' -> finds AAA.BBB
16020
16021      */
16022     
16023     toObject : function(str)
16024     {
16025         if (!str || typeof(str) == 'object') {
16026             return str;
16027         }
16028         if (str.substring(0,1) == '#') {
16029             return str;
16030         }
16031
16032         var ar = str.split('.');
16033         var rt, o;
16034         rt = ar.shift();
16035             /** eval:var:o */
16036         try {
16037             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16038         } catch (e) {
16039             throw "Module not found : " + str;
16040         }
16041         
16042         if (o === false) {
16043             throw "Module not found : " + str;
16044         }
16045         Roo.each(ar, function(e) {
16046             if (typeof(o[e]) == 'undefined') {
16047                 throw "Module not found : " + str;
16048             }
16049             o = o[e];
16050         });
16051         
16052         return o;
16053         
16054     },
16055     
16056     
16057     /**
16058      * move modules into their correct place in the tree..
16059      * 
16060      */
16061     preBuild : function ()
16062     {
16063         var _t = this;
16064         Roo.each(this.modules , function (obj)
16065         {
16066             Roo.XComponent.event.fireEvent('beforebuild', obj);
16067             
16068             var opar = obj.parent;
16069             try { 
16070                 obj.parent = this.toObject(opar);
16071             } catch(e) {
16072                 Roo.log("parent:toObject failed: " + e.toString());
16073                 return;
16074             }
16075             
16076             if (!obj.parent) {
16077                 Roo.debug && Roo.log("GOT top level module");
16078                 Roo.debug && Roo.log(obj);
16079                 obj.modules = new Roo.util.MixedCollection(false, 
16080                     function(o) { return o.order + '' }
16081                 );
16082                 this.topModule = obj;
16083                 return;
16084             }
16085                         // parent is a string (usually a dom element name..)
16086             if (typeof(obj.parent) == 'string') {
16087                 this.elmodules.push(obj);
16088                 return;
16089             }
16090             if (obj.parent.constructor != Roo.XComponent) {
16091                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16092             }
16093             if (!obj.parent.modules) {
16094                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16095                     function(o) { return o.order + '' }
16096                 );
16097             }
16098             if (obj.parent.disabled) {
16099                 obj.disabled = true;
16100             }
16101             obj.parent.modules.add(obj);
16102         }, this);
16103     },
16104     
16105      /**
16106      * make a list of modules to build.
16107      * @return {Array} list of modules. 
16108      */ 
16109     
16110     buildOrder : function()
16111     {
16112         var _this = this;
16113         var cmp = function(a,b) {   
16114             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16115         };
16116         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16117             throw "No top level modules to build";
16118         }
16119         
16120         // make a flat list in order of modules to build.
16121         var mods = this.topModule ? [ this.topModule ] : [];
16122                 
16123         
16124         // elmodules (is a list of DOM based modules )
16125         Roo.each(this.elmodules, function(e) {
16126             mods.push(e);
16127             if (!this.topModule &&
16128                 typeof(e.parent) == 'string' &&
16129                 e.parent.substring(0,1) == '#' &&
16130                 Roo.get(e.parent.substr(1))
16131                ) {
16132                 
16133                 _this.topModule = e;
16134             }
16135             
16136         });
16137
16138         
16139         // add modules to their parents..
16140         var addMod = function(m) {
16141             Roo.debug && Roo.log("build Order: add: " + m.name);
16142                 
16143             mods.push(m);
16144             if (m.modules && !m.disabled) {
16145                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16146                 m.modules.keySort('ASC',  cmp );
16147                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16148     
16149                 m.modules.each(addMod);
16150             } else {
16151                 Roo.debug && Roo.log("build Order: no child modules");
16152             }
16153             // not sure if this is used any more..
16154             if (m.finalize) {
16155                 m.finalize.name = m.name + " (clean up) ";
16156                 mods.push(m.finalize);
16157             }
16158             
16159         }
16160         if (this.topModule && this.topModule.modules) { 
16161             this.topModule.modules.keySort('ASC',  cmp );
16162             this.topModule.modules.each(addMod);
16163         } 
16164         return mods;
16165     },
16166     
16167      /**
16168      * Build the registered modules.
16169      * @param {Object} parent element.
16170      * @param {Function} optional method to call after module has been added.
16171      * 
16172      */ 
16173    
16174     build : function(opts) 
16175     {
16176         
16177         if (typeof(opts) != 'undefined') {
16178             Roo.apply(this,opts);
16179         }
16180         
16181         this.preBuild();
16182         var mods = this.buildOrder();
16183       
16184         //this.allmods = mods;
16185         //Roo.debug && Roo.log(mods);
16186         //return;
16187         if (!mods.length) { // should not happen
16188             throw "NO modules!!!";
16189         }
16190         
16191         
16192         var msg = "Building Interface...";
16193         // flash it up as modal - so we store the mask!?
16194         if (!this.hideProgress && Roo.MessageBox) {
16195             Roo.MessageBox.show({ title: 'loading' });
16196             Roo.MessageBox.show({
16197                title: "Please wait...",
16198                msg: msg,
16199                width:450,
16200                progress:true,
16201                closable:false,
16202                modal: false
16203               
16204             });
16205         }
16206         var total = mods.length;
16207         
16208         var _this = this;
16209         var progressRun = function() {
16210             if (!mods.length) {
16211                 Roo.debug && Roo.log('hide?');
16212                 if (!this.hideProgress && Roo.MessageBox) {
16213                     Roo.MessageBox.hide();
16214                 }
16215                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16216                 
16217                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16218                 
16219                 // THE END...
16220                 return false;   
16221             }
16222             
16223             var m = mods.shift();
16224             
16225             
16226             Roo.debug && Roo.log(m);
16227             // not sure if this is supported any more.. - modules that are are just function
16228             if (typeof(m) == 'function') { 
16229                 m.call(this);
16230                 return progressRun.defer(10, _this);
16231             } 
16232             
16233             
16234             msg = "Building Interface " + (total  - mods.length) + 
16235                     " of " + total + 
16236                     (m.name ? (' - ' + m.name) : '');
16237                         Roo.debug && Roo.log(msg);
16238             if (!this.hideProgress &&  Roo.MessageBox) { 
16239                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16240             }
16241             
16242          
16243             // is the module disabled?
16244             var disabled = (typeof(m.disabled) == 'function') ?
16245                 m.disabled.call(m.module.disabled) : m.disabled;    
16246             
16247             
16248             if (disabled) {
16249                 return progressRun(); // we do not update the display!
16250             }
16251             
16252             // now build 
16253             
16254                         
16255                         
16256             m.render();
16257             // it's 10 on top level, and 1 on others??? why...
16258             return progressRun.defer(10, _this);
16259              
16260         }
16261         progressRun.defer(1, _this);
16262      
16263         
16264         
16265     },
16266         
16267         
16268         /**
16269          * Event Object.
16270          *
16271          *
16272          */
16273         event: false, 
16274     /**
16275          * wrapper for event.on - aliased later..  
16276          * Typically use to register a event handler for register:
16277          *
16278          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16279          *
16280          */
16281     on : false
16282    
16283     
16284     
16285 });
16286
16287 Roo.XComponent.event = new Roo.util.Observable({
16288                 events : { 
16289                         /**
16290                          * @event register
16291                          * Fires when an Component is registered,
16292                          * set the disable property on the Component to stop registration.
16293                          * @param {Roo.XComponent} c the component being registerd.
16294                          * 
16295                          */
16296                         'register' : true,
16297             /**
16298                          * @event beforebuild
16299                          * Fires before each Component is built
16300                          * can be used to apply permissions.
16301                          * @param {Roo.XComponent} c the component being registerd.
16302                          * 
16303                          */
16304                         'beforebuild' : true,
16305                         /**
16306                          * @event buildcomplete
16307                          * Fires on the top level element when all elements have been built
16308                          * @param {Roo.XComponent} the top level component.
16309                          */
16310                         'buildcomplete' : true
16311                         
16312                 }
16313 });
16314
16315 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16316  /*
16317  * Based on:
16318  * Ext JS Library 1.1.1
16319  * Copyright(c) 2006-2007, Ext JS, LLC.
16320  *
16321  * Originally Released Under LGPL - original licence link has changed is not relivant.
16322  *
16323  * Fork - LGPL
16324  * <script type="text/javascript">
16325  */
16326
16327
16328
16329 /*
16330  * These classes are derivatives of the similarly named classes in the YUI Library.
16331  * The original license:
16332  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16333  * Code licensed under the BSD License:
16334  * http://developer.yahoo.net/yui/license.txt
16335  */
16336
16337 (function() {
16338
16339 var Event=Roo.EventManager;
16340 var Dom=Roo.lib.Dom;
16341
16342 /**
16343  * @class Roo.dd.DragDrop
16344  * @extends Roo.util.Observable
16345  * Defines the interface and base operation of items that that can be
16346  * dragged or can be drop targets.  It was designed to be extended, overriding
16347  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16348  * Up to three html elements can be associated with a DragDrop instance:
16349  * <ul>
16350  * <li>linked element: the element that is passed into the constructor.
16351  * This is the element which defines the boundaries for interaction with
16352  * other DragDrop objects.</li>
16353  * <li>handle element(s): The drag operation only occurs if the element that
16354  * was clicked matches a handle element.  By default this is the linked
16355  * element, but there are times that you will want only a portion of the
16356  * linked element to initiate the drag operation, and the setHandleElId()
16357  * method provides a way to define this.</li>
16358  * <li>drag element: this represents the element that would be moved along
16359  * with the cursor during a drag operation.  By default, this is the linked
16360  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16361  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16362  * </li>
16363  * </ul>
16364  * This class should not be instantiated until the onload event to ensure that
16365  * the associated elements are available.
16366  * The following would define a DragDrop obj that would interact with any
16367  * other DragDrop obj in the "group1" group:
16368  * <pre>
16369  *  dd = new Roo.dd.DragDrop("div1", "group1");
16370  * </pre>
16371  * Since none of the event handlers have been implemented, nothing would
16372  * actually happen if you were to run the code above.  Normally you would
16373  * override this class or one of the default implementations, but you can
16374  * also override the methods you want on an instance of the class...
16375  * <pre>
16376  *  dd.onDragDrop = function(e, id) {
16377  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16378  *  }
16379  * </pre>
16380  * @constructor
16381  * @param {String} id of the element that is linked to this instance
16382  * @param {String} sGroup the group of related DragDrop objects
16383  * @param {object} config an object containing configurable attributes
16384  *                Valid properties for DragDrop:
16385  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16386  */
16387 Roo.dd.DragDrop = function(id, sGroup, config) {
16388     if (id) {
16389         this.init(id, sGroup, config);
16390     }
16391     
16392 };
16393
16394 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16395
16396     /**
16397      * The id of the element associated with this object.  This is what we
16398      * refer to as the "linked element" because the size and position of
16399      * this element is used to determine when the drag and drop objects have
16400      * interacted.
16401      * @property id
16402      * @type String
16403      */
16404     id: null,
16405
16406     /**
16407      * Configuration attributes passed into the constructor
16408      * @property config
16409      * @type object
16410      */
16411     config: null,
16412
16413     /**
16414      * The id of the element that will be dragged.  By default this is same
16415      * as the linked element , but could be changed to another element. Ex:
16416      * Roo.dd.DDProxy
16417      * @property dragElId
16418      * @type String
16419      * @private
16420      */
16421     dragElId: null,
16422
16423     /**
16424      * the id of the element that initiates the drag operation.  By default
16425      * this is the linked element, but could be changed to be a child of this
16426      * element.  This lets us do things like only starting the drag when the
16427      * header element within the linked html element is clicked.
16428      * @property handleElId
16429      * @type String
16430      * @private
16431      */
16432     handleElId: null,
16433
16434     /**
16435      * An associative array of HTML tags that will be ignored if clicked.
16436      * @property invalidHandleTypes
16437      * @type {string: string}
16438      */
16439     invalidHandleTypes: null,
16440
16441     /**
16442      * An associative array of ids for elements that will be ignored if clicked
16443      * @property invalidHandleIds
16444      * @type {string: string}
16445      */
16446     invalidHandleIds: null,
16447
16448     /**
16449      * An indexted array of css class names for elements that will be ignored
16450      * if clicked.
16451      * @property invalidHandleClasses
16452      * @type string[]
16453      */
16454     invalidHandleClasses: null,
16455
16456     /**
16457      * The linked element's absolute X position at the time the drag was
16458      * started
16459      * @property startPageX
16460      * @type int
16461      * @private
16462      */
16463     startPageX: 0,
16464
16465     /**
16466      * The linked element's absolute X position at the time the drag was
16467      * started
16468      * @property startPageY
16469      * @type int
16470      * @private
16471      */
16472     startPageY: 0,
16473
16474     /**
16475      * The group defines a logical collection of DragDrop objects that are
16476      * related.  Instances only get events when interacting with other
16477      * DragDrop object in the same group.  This lets us define multiple
16478      * groups using a single DragDrop subclass if we want.
16479      * @property groups
16480      * @type {string: string}
16481      */
16482     groups: null,
16483
16484     /**
16485      * Individual drag/drop instances can be locked.  This will prevent
16486      * onmousedown start drag.
16487      * @property locked
16488      * @type boolean
16489      * @private
16490      */
16491     locked: false,
16492
16493     /**
16494      * Lock this instance
16495      * @method lock
16496      */
16497     lock: function() { this.locked = true; },
16498
16499     /**
16500      * Unlock this instace
16501      * @method unlock
16502      */
16503     unlock: function() { this.locked = false; },
16504
16505     /**
16506      * By default, all insances can be a drop target.  This can be disabled by
16507      * setting isTarget to false.
16508      * @method isTarget
16509      * @type boolean
16510      */
16511     isTarget: true,
16512
16513     /**
16514      * The padding configured for this drag and drop object for calculating
16515      * the drop zone intersection with this object.
16516      * @method padding
16517      * @type int[]
16518      */
16519     padding: null,
16520
16521     /**
16522      * Cached reference to the linked element
16523      * @property _domRef
16524      * @private
16525      */
16526     _domRef: null,
16527
16528     /**
16529      * Internal typeof flag
16530      * @property __ygDragDrop
16531      * @private
16532      */
16533     __ygDragDrop: true,
16534
16535     /**
16536      * Set to true when horizontal contraints are applied
16537      * @property constrainX
16538      * @type boolean
16539      * @private
16540      */
16541     constrainX: false,
16542
16543     /**
16544      * Set to true when vertical contraints are applied
16545      * @property constrainY
16546      * @type boolean
16547      * @private
16548      */
16549     constrainY: false,
16550
16551     /**
16552      * The left constraint
16553      * @property minX
16554      * @type int
16555      * @private
16556      */
16557     minX: 0,
16558
16559     /**
16560      * The right constraint
16561      * @property maxX
16562      * @type int
16563      * @private
16564      */
16565     maxX: 0,
16566
16567     /**
16568      * The up constraint
16569      * @property minY
16570      * @type int
16571      * @type int
16572      * @private
16573      */
16574     minY: 0,
16575
16576     /**
16577      * The down constraint
16578      * @property maxY
16579      * @type int
16580      * @private
16581      */
16582     maxY: 0,
16583
16584     /**
16585      * Maintain offsets when we resetconstraints.  Set to true when you want
16586      * the position of the element relative to its parent to stay the same
16587      * when the page changes
16588      *
16589      * @property maintainOffset
16590      * @type boolean
16591      */
16592     maintainOffset: false,
16593
16594     /**
16595      * Array of pixel locations the element will snap to if we specified a
16596      * horizontal graduation/interval.  This array is generated automatically
16597      * when you define a tick interval.
16598      * @property xTicks
16599      * @type int[]
16600      */
16601     xTicks: null,
16602
16603     /**
16604      * Array of pixel locations the element will snap to if we specified a
16605      * vertical graduation/interval.  This array is generated automatically
16606      * when you define a tick interval.
16607      * @property yTicks
16608      * @type int[]
16609      */
16610     yTicks: null,
16611
16612     /**
16613      * By default the drag and drop instance will only respond to the primary
16614      * button click (left button for a right-handed mouse).  Set to true to
16615      * allow drag and drop to start with any mouse click that is propogated
16616      * by the browser
16617      * @property primaryButtonOnly
16618      * @type boolean
16619      */
16620     primaryButtonOnly: true,
16621
16622     /**
16623      * The availabe property is false until the linked dom element is accessible.
16624      * @property available
16625      * @type boolean
16626      */
16627     available: false,
16628
16629     /**
16630      * By default, drags can only be initiated if the mousedown occurs in the
16631      * region the linked element is.  This is done in part to work around a
16632      * bug in some browsers that mis-report the mousedown if the previous
16633      * mouseup happened outside of the window.  This property is set to true
16634      * if outer handles are defined.
16635      *
16636      * @property hasOuterHandles
16637      * @type boolean
16638      * @default false
16639      */
16640     hasOuterHandles: false,
16641
16642     /**
16643      * Code that executes immediately before the startDrag event
16644      * @method b4StartDrag
16645      * @private
16646      */
16647     b4StartDrag: function(x, y) { },
16648
16649     /**
16650      * Abstract method called after a drag/drop object is clicked
16651      * and the drag or mousedown time thresholds have beeen met.
16652      * @method startDrag
16653      * @param {int} X click location
16654      * @param {int} Y click location
16655      */
16656     startDrag: function(x, y) { /* override this */ },
16657
16658     /**
16659      * Code that executes immediately before the onDrag event
16660      * @method b4Drag
16661      * @private
16662      */
16663     b4Drag: function(e) { },
16664
16665     /**
16666      * Abstract method called during the onMouseMove event while dragging an
16667      * object.
16668      * @method onDrag
16669      * @param {Event} e the mousemove event
16670      */
16671     onDrag: function(e) { /* override this */ },
16672
16673     /**
16674      * Abstract method called when this element fist begins hovering over
16675      * another DragDrop obj
16676      * @method onDragEnter
16677      * @param {Event} e the mousemove event
16678      * @param {String|DragDrop[]} id In POINT mode, the element
16679      * id this is hovering over.  In INTERSECT mode, an array of one or more
16680      * dragdrop items being hovered over.
16681      */
16682     onDragEnter: function(e, id) { /* override this */ },
16683
16684     /**
16685      * Code that executes immediately before the onDragOver event
16686      * @method b4DragOver
16687      * @private
16688      */
16689     b4DragOver: function(e) { },
16690
16691     /**
16692      * Abstract method called when this element is hovering over another
16693      * DragDrop obj
16694      * @method onDragOver
16695      * @param {Event} e the mousemove event
16696      * @param {String|DragDrop[]} id In POINT mode, the element
16697      * id this is hovering over.  In INTERSECT mode, an array of dd items
16698      * being hovered over.
16699      */
16700     onDragOver: function(e, id) { /* override this */ },
16701
16702     /**
16703      * Code that executes immediately before the onDragOut event
16704      * @method b4DragOut
16705      * @private
16706      */
16707     b4DragOut: function(e) { },
16708
16709     /**
16710      * Abstract method called when we are no longer hovering over an element
16711      * @method onDragOut
16712      * @param {Event} e the mousemove event
16713      * @param {String|DragDrop[]} id In POINT mode, the element
16714      * id this was hovering over.  In INTERSECT mode, an array of dd items
16715      * that the mouse is no longer over.
16716      */
16717     onDragOut: function(e, id) { /* override this */ },
16718
16719     /**
16720      * Code that executes immediately before the onDragDrop event
16721      * @method b4DragDrop
16722      * @private
16723      */
16724     b4DragDrop: function(e) { },
16725
16726     /**
16727      * Abstract method called when this item is dropped on another DragDrop
16728      * obj
16729      * @method onDragDrop
16730      * @param {Event} e the mouseup event
16731      * @param {String|DragDrop[]} id In POINT mode, the element
16732      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16733      * was dropped on.
16734      */
16735     onDragDrop: function(e, id) { /* override this */ },
16736
16737     /**
16738      * Abstract method called when this item is dropped on an area with no
16739      * drop target
16740      * @method onInvalidDrop
16741      * @param {Event} e the mouseup event
16742      */
16743     onInvalidDrop: function(e) { /* override this */ },
16744
16745     /**
16746      * Code that executes immediately before the endDrag event
16747      * @method b4EndDrag
16748      * @private
16749      */
16750     b4EndDrag: function(e) { },
16751
16752     /**
16753      * Fired when we are done dragging the object
16754      * @method endDrag
16755      * @param {Event} e the mouseup event
16756      */
16757     endDrag: function(e) { /* override this */ },
16758
16759     /**
16760      * Code executed immediately before the onMouseDown event
16761      * @method b4MouseDown
16762      * @param {Event} e the mousedown event
16763      * @private
16764      */
16765     b4MouseDown: function(e) {  },
16766
16767     /**
16768      * Event handler that fires when a drag/drop obj gets a mousedown
16769      * @method onMouseDown
16770      * @param {Event} e the mousedown event
16771      */
16772     onMouseDown: function(e) { /* override this */ },
16773
16774     /**
16775      * Event handler that fires when a drag/drop obj gets a mouseup
16776      * @method onMouseUp
16777      * @param {Event} e the mouseup event
16778      */
16779     onMouseUp: function(e) { /* override this */ },
16780
16781     /**
16782      * Override the onAvailable method to do what is needed after the initial
16783      * position was determined.
16784      * @method onAvailable
16785      */
16786     onAvailable: function () {
16787     },
16788
16789     /*
16790      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16791      * @type Object
16792      */
16793     defaultPadding : {left:0, right:0, top:0, bottom:0},
16794
16795     /*
16796      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16797  *
16798  * Usage:
16799  <pre><code>
16800  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16801                 { dragElId: "existingProxyDiv" });
16802  dd.startDrag = function(){
16803      this.constrainTo("parent-id");
16804  };
16805  </code></pre>
16806  * Or you can initalize it using the {@link Roo.Element} object:
16807  <pre><code>
16808  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16809      startDrag : function(){
16810          this.constrainTo("parent-id");
16811      }
16812  });
16813  </code></pre>
16814      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16815      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16816      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16817      * an object containing the sides to pad. For example: {right:10, bottom:10}
16818      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16819      */
16820     constrainTo : function(constrainTo, pad, inContent){
16821         if(typeof pad == "number"){
16822             pad = {left: pad, right:pad, top:pad, bottom:pad};
16823         }
16824         pad = pad || this.defaultPadding;
16825         var b = Roo.get(this.getEl()).getBox();
16826         var ce = Roo.get(constrainTo);
16827         var s = ce.getScroll();
16828         var c, cd = ce.dom;
16829         if(cd == document.body){
16830             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16831         }else{
16832             xy = ce.getXY();
16833             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16834         }
16835
16836
16837         var topSpace = b.y - c.y;
16838         var leftSpace = b.x - c.x;
16839
16840         this.resetConstraints();
16841         this.setXConstraint(leftSpace - (pad.left||0), // left
16842                 c.width - leftSpace - b.width - (pad.right||0) //right
16843         );
16844         this.setYConstraint(topSpace - (pad.top||0), //top
16845                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16846         );
16847     },
16848
16849     /**
16850      * Returns a reference to the linked element
16851      * @method getEl
16852      * @return {HTMLElement} the html element
16853      */
16854     getEl: function() {
16855         if (!this._domRef) {
16856             this._domRef = Roo.getDom(this.id);
16857         }
16858
16859         return this._domRef;
16860     },
16861
16862     /**
16863      * Returns a reference to the actual element to drag.  By default this is
16864      * the same as the html element, but it can be assigned to another
16865      * element. An example of this can be found in Roo.dd.DDProxy
16866      * @method getDragEl
16867      * @return {HTMLElement} the html element
16868      */
16869     getDragEl: function() {
16870         return Roo.getDom(this.dragElId);
16871     },
16872
16873     /**
16874      * Sets up the DragDrop object.  Must be called in the constructor of any
16875      * Roo.dd.DragDrop subclass
16876      * @method init
16877      * @param id the id of the linked element
16878      * @param {String} sGroup the group of related items
16879      * @param {object} config configuration attributes
16880      */
16881     init: function(id, sGroup, config) {
16882         this.initTarget(id, sGroup, config);
16883         if (!Roo.isTouch) {
16884             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16885         }
16886         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16887         // Event.on(this.id, "selectstart", Event.preventDefault);
16888     },
16889
16890     /**
16891      * Initializes Targeting functionality only... the object does not
16892      * get a mousedown handler.
16893      * @method initTarget
16894      * @param id the id of the linked element
16895      * @param {String} sGroup the group of related items
16896      * @param {object} config configuration attributes
16897      */
16898     initTarget: function(id, sGroup, config) {
16899
16900         // configuration attributes
16901         this.config = config || {};
16902
16903         // create a local reference to the drag and drop manager
16904         this.DDM = Roo.dd.DDM;
16905         // initialize the groups array
16906         this.groups = {};
16907
16908         // assume that we have an element reference instead of an id if the
16909         // parameter is not a string
16910         if (typeof id !== "string") {
16911             id = Roo.id(id);
16912         }
16913
16914         // set the id
16915         this.id = id;
16916
16917         // add to an interaction group
16918         this.addToGroup((sGroup) ? sGroup : "default");
16919
16920         // We don't want to register this as the handle with the manager
16921         // so we just set the id rather than calling the setter.
16922         this.handleElId = id;
16923
16924         // the linked element is the element that gets dragged by default
16925         this.setDragElId(id);
16926
16927         // by default, clicked anchors will not start drag operations.
16928         this.invalidHandleTypes = { A: "A" };
16929         this.invalidHandleIds = {};
16930         this.invalidHandleClasses = [];
16931
16932         this.applyConfig();
16933
16934         this.handleOnAvailable();
16935     },
16936
16937     /**
16938      * Applies the configuration parameters that were passed into the constructor.
16939      * This is supposed to happen at each level through the inheritance chain.  So
16940      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16941      * DragDrop in order to get all of the parameters that are available in
16942      * each object.
16943      * @method applyConfig
16944      */
16945     applyConfig: function() {
16946
16947         // configurable properties:
16948         //    padding, isTarget, maintainOffset, primaryButtonOnly
16949         this.padding           = this.config.padding || [0, 0, 0, 0];
16950         this.isTarget          = (this.config.isTarget !== false);
16951         this.maintainOffset    = (this.config.maintainOffset);
16952         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16953
16954     },
16955
16956     /**
16957      * Executed when the linked element is available
16958      * @method handleOnAvailable
16959      * @private
16960      */
16961     handleOnAvailable: function() {
16962         this.available = true;
16963         this.resetConstraints();
16964         this.onAvailable();
16965     },
16966
16967      /**
16968      * Configures the padding for the target zone in px.  Effectively expands
16969      * (or reduces) the virtual object size for targeting calculations.
16970      * Supports css-style shorthand; if only one parameter is passed, all sides
16971      * will have that padding, and if only two are passed, the top and bottom
16972      * will have the first param, the left and right the second.
16973      * @method setPadding
16974      * @param {int} iTop    Top pad
16975      * @param {int} iRight  Right pad
16976      * @param {int} iBot    Bot pad
16977      * @param {int} iLeft   Left pad
16978      */
16979     setPadding: function(iTop, iRight, iBot, iLeft) {
16980         // this.padding = [iLeft, iRight, iTop, iBot];
16981         if (!iRight && 0 !== iRight) {
16982             this.padding = [iTop, iTop, iTop, iTop];
16983         } else if (!iBot && 0 !== iBot) {
16984             this.padding = [iTop, iRight, iTop, iRight];
16985         } else {
16986             this.padding = [iTop, iRight, iBot, iLeft];
16987         }
16988     },
16989
16990     /**
16991      * Stores the initial placement of the linked element.
16992      * @method setInitialPosition
16993      * @param {int} diffX   the X offset, default 0
16994      * @param {int} diffY   the Y offset, default 0
16995      */
16996     setInitPosition: function(diffX, diffY) {
16997         var el = this.getEl();
16998
16999         if (!this.DDM.verifyEl(el)) {
17000             return;
17001         }
17002
17003         var dx = diffX || 0;
17004         var dy = diffY || 0;
17005
17006         var p = Dom.getXY( el );
17007
17008         this.initPageX = p[0] - dx;
17009         this.initPageY = p[1] - dy;
17010
17011         this.lastPageX = p[0];
17012         this.lastPageY = p[1];
17013
17014
17015         this.setStartPosition(p);
17016     },
17017
17018     /**
17019      * Sets the start position of the element.  This is set when the obj
17020      * is initialized, the reset when a drag is started.
17021      * @method setStartPosition
17022      * @param pos current position (from previous lookup)
17023      * @private
17024      */
17025     setStartPosition: function(pos) {
17026         var p = pos || Dom.getXY( this.getEl() );
17027         this.deltaSetXY = null;
17028
17029         this.startPageX = p[0];
17030         this.startPageY = p[1];
17031     },
17032
17033     /**
17034      * Add this instance to a group of related drag/drop objects.  All
17035      * instances belong to at least one group, and can belong to as many
17036      * groups as needed.
17037      * @method addToGroup
17038      * @param sGroup {string} the name of the group
17039      */
17040     addToGroup: function(sGroup) {
17041         this.groups[sGroup] = true;
17042         this.DDM.regDragDrop(this, sGroup);
17043     },
17044
17045     /**
17046      * Remove's this instance from the supplied interaction group
17047      * @method removeFromGroup
17048      * @param {string}  sGroup  The group to drop
17049      */
17050     removeFromGroup: function(sGroup) {
17051         if (this.groups[sGroup]) {
17052             delete this.groups[sGroup];
17053         }
17054
17055         this.DDM.removeDDFromGroup(this, sGroup);
17056     },
17057
17058     /**
17059      * Allows you to specify that an element other than the linked element
17060      * will be moved with the cursor during a drag
17061      * @method setDragElId
17062      * @param id {string} the id of the element that will be used to initiate the drag
17063      */
17064     setDragElId: function(id) {
17065         this.dragElId = id;
17066     },
17067
17068     /**
17069      * Allows you to specify a child of the linked element that should be
17070      * used to initiate the drag operation.  An example of this would be if
17071      * you have a content div with text and links.  Clicking anywhere in the
17072      * content area would normally start the drag operation.  Use this method
17073      * to specify that an element inside of the content div is the element
17074      * that starts the drag operation.
17075      * @method setHandleElId
17076      * @param id {string} the id of the element that will be used to
17077      * initiate the drag.
17078      */
17079     setHandleElId: function(id) {
17080         if (typeof id !== "string") {
17081             id = Roo.id(id);
17082         }
17083         this.handleElId = id;
17084         this.DDM.regHandle(this.id, id);
17085     },
17086
17087     /**
17088      * Allows you to set an element outside of the linked element as a drag
17089      * handle
17090      * @method setOuterHandleElId
17091      * @param id the id of the element that will be used to initiate the drag
17092      */
17093     setOuterHandleElId: function(id) {
17094         if (typeof id !== "string") {
17095             id = Roo.id(id);
17096         }
17097         Event.on(id, "mousedown",
17098                 this.handleMouseDown, this);
17099         this.setHandleElId(id);
17100
17101         this.hasOuterHandles = true;
17102     },
17103
17104     /**
17105      * Remove all drag and drop hooks for this element
17106      * @method unreg
17107      */
17108     unreg: function() {
17109         Event.un(this.id, "mousedown",
17110                 this.handleMouseDown);
17111         Event.un(this.id, "touchstart",
17112                 this.handleMouseDown);
17113         this._domRef = null;
17114         this.DDM._remove(this);
17115     },
17116
17117     destroy : function(){
17118         this.unreg();
17119     },
17120
17121     /**
17122      * Returns true if this instance is locked, or the drag drop mgr is locked
17123      * (meaning that all drag/drop is disabled on the page.)
17124      * @method isLocked
17125      * @return {boolean} true if this obj or all drag/drop is locked, else
17126      * false
17127      */
17128     isLocked: function() {
17129         return (this.DDM.isLocked() || this.locked);
17130     },
17131
17132     /**
17133      * Fired when this object is clicked
17134      * @method handleMouseDown
17135      * @param {Event} e
17136      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17137      * @private
17138      */
17139     handleMouseDown: function(e, oDD){
17140      
17141         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17142             //Roo.log('not touch/ button !=0');
17143             return;
17144         }
17145         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17146             return; // double touch..
17147         }
17148         
17149
17150         if (this.isLocked()) {
17151             //Roo.log('locked');
17152             return;
17153         }
17154
17155         this.DDM.refreshCache(this.groups);
17156 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17157         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17158         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17159             //Roo.log('no outer handes or not over target');
17160                 // do nothing.
17161         } else {
17162 //            Roo.log('check validator');
17163             if (this.clickValidator(e)) {
17164 //                Roo.log('validate success');
17165                 // set the initial element position
17166                 this.setStartPosition();
17167
17168
17169                 this.b4MouseDown(e);
17170                 this.onMouseDown(e);
17171
17172                 this.DDM.handleMouseDown(e, this);
17173
17174                 this.DDM.stopEvent(e);
17175             } else {
17176
17177
17178             }
17179         }
17180     },
17181
17182     clickValidator: function(e) {
17183         var target = e.getTarget();
17184         return ( this.isValidHandleChild(target) &&
17185                     (this.id == this.handleElId ||
17186                         this.DDM.handleWasClicked(target, this.id)) );
17187     },
17188
17189     /**
17190      * Allows you to specify a tag name that should not start a drag operation
17191      * when clicked.  This is designed to facilitate embedding links within a
17192      * drag handle that do something other than start the drag.
17193      * @method addInvalidHandleType
17194      * @param {string} tagName the type of element to exclude
17195      */
17196     addInvalidHandleType: function(tagName) {
17197         var type = tagName.toUpperCase();
17198         this.invalidHandleTypes[type] = type;
17199     },
17200
17201     /**
17202      * Lets you to specify an element id for a child of a drag handle
17203      * that should not initiate a drag
17204      * @method addInvalidHandleId
17205      * @param {string} id the element id of the element you wish to ignore
17206      */
17207     addInvalidHandleId: function(id) {
17208         if (typeof id !== "string") {
17209             id = Roo.id(id);
17210         }
17211         this.invalidHandleIds[id] = id;
17212     },
17213
17214     /**
17215      * Lets you specify a css class of elements that will not initiate a drag
17216      * @method addInvalidHandleClass
17217      * @param {string} cssClass the class of the elements you wish to ignore
17218      */
17219     addInvalidHandleClass: function(cssClass) {
17220         this.invalidHandleClasses.push(cssClass);
17221     },
17222
17223     /**
17224      * Unsets an excluded tag name set by addInvalidHandleType
17225      * @method removeInvalidHandleType
17226      * @param {string} tagName the type of element to unexclude
17227      */
17228     removeInvalidHandleType: function(tagName) {
17229         var type = tagName.toUpperCase();
17230         // this.invalidHandleTypes[type] = null;
17231         delete this.invalidHandleTypes[type];
17232     },
17233
17234     /**
17235      * Unsets an invalid handle id
17236      * @method removeInvalidHandleId
17237      * @param {string} id the id of the element to re-enable
17238      */
17239     removeInvalidHandleId: function(id) {
17240         if (typeof id !== "string") {
17241             id = Roo.id(id);
17242         }
17243         delete this.invalidHandleIds[id];
17244     },
17245
17246     /**
17247      * Unsets an invalid css class
17248      * @method removeInvalidHandleClass
17249      * @param {string} cssClass the class of the element(s) you wish to
17250      * re-enable
17251      */
17252     removeInvalidHandleClass: function(cssClass) {
17253         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17254             if (this.invalidHandleClasses[i] == cssClass) {
17255                 delete this.invalidHandleClasses[i];
17256             }
17257         }
17258     },
17259
17260     /**
17261      * Checks the tag exclusion list to see if this click should be ignored
17262      * @method isValidHandleChild
17263      * @param {HTMLElement} node the HTMLElement to evaluate
17264      * @return {boolean} true if this is a valid tag type, false if not
17265      */
17266     isValidHandleChild: function(node) {
17267
17268         var valid = true;
17269         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17270         var nodeName;
17271         try {
17272             nodeName = node.nodeName.toUpperCase();
17273         } catch(e) {
17274             nodeName = node.nodeName;
17275         }
17276         valid = valid && !this.invalidHandleTypes[nodeName];
17277         valid = valid && !this.invalidHandleIds[node.id];
17278
17279         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17280             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17281         }
17282
17283
17284         return valid;
17285
17286     },
17287
17288     /**
17289      * Create the array of horizontal tick marks if an interval was specified
17290      * in setXConstraint().
17291      * @method setXTicks
17292      * @private
17293      */
17294     setXTicks: function(iStartX, iTickSize) {
17295         this.xTicks = [];
17296         this.xTickSize = iTickSize;
17297
17298         var tickMap = {};
17299
17300         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17301             if (!tickMap[i]) {
17302                 this.xTicks[this.xTicks.length] = i;
17303                 tickMap[i] = true;
17304             }
17305         }
17306
17307         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17308             if (!tickMap[i]) {
17309                 this.xTicks[this.xTicks.length] = i;
17310                 tickMap[i] = true;
17311             }
17312         }
17313
17314         this.xTicks.sort(this.DDM.numericSort) ;
17315     },
17316
17317     /**
17318      * Create the array of vertical tick marks if an interval was specified in
17319      * setYConstraint().
17320      * @method setYTicks
17321      * @private
17322      */
17323     setYTicks: function(iStartY, iTickSize) {
17324         this.yTicks = [];
17325         this.yTickSize = iTickSize;
17326
17327         var tickMap = {};
17328
17329         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17330             if (!tickMap[i]) {
17331                 this.yTicks[this.yTicks.length] = i;
17332                 tickMap[i] = true;
17333             }
17334         }
17335
17336         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17337             if (!tickMap[i]) {
17338                 this.yTicks[this.yTicks.length] = i;
17339                 tickMap[i] = true;
17340             }
17341         }
17342
17343         this.yTicks.sort(this.DDM.numericSort) ;
17344     },
17345
17346     /**
17347      * By default, the element can be dragged any place on the screen.  Use
17348      * this method to limit the horizontal travel of the element.  Pass in
17349      * 0,0 for the parameters if you want to lock the drag to the y axis.
17350      * @method setXConstraint
17351      * @param {int} iLeft the number of pixels the element can move to the left
17352      * @param {int} iRight the number of pixels the element can move to the
17353      * right
17354      * @param {int} iTickSize optional parameter for specifying that the
17355      * element
17356      * should move iTickSize pixels at a time.
17357      */
17358     setXConstraint: function(iLeft, iRight, iTickSize) {
17359         this.leftConstraint = iLeft;
17360         this.rightConstraint = iRight;
17361
17362         this.minX = this.initPageX - iLeft;
17363         this.maxX = this.initPageX + iRight;
17364         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17365
17366         this.constrainX = true;
17367     },
17368
17369     /**
17370      * Clears any constraints applied to this instance.  Also clears ticks
17371      * since they can't exist independent of a constraint at this time.
17372      * @method clearConstraints
17373      */
17374     clearConstraints: function() {
17375         this.constrainX = false;
17376         this.constrainY = false;
17377         this.clearTicks();
17378     },
17379
17380     /**
17381      * Clears any tick interval defined for this instance
17382      * @method clearTicks
17383      */
17384     clearTicks: function() {
17385         this.xTicks = null;
17386         this.yTicks = null;
17387         this.xTickSize = 0;
17388         this.yTickSize = 0;
17389     },
17390
17391     /**
17392      * By default, the element can be dragged any place on the screen.  Set
17393      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17394      * parameters if you want to lock the drag to the x axis.
17395      * @method setYConstraint
17396      * @param {int} iUp the number of pixels the element can move up
17397      * @param {int} iDown the number of pixels the element can move down
17398      * @param {int} iTickSize optional parameter for specifying that the
17399      * element should move iTickSize pixels at a time.
17400      */
17401     setYConstraint: function(iUp, iDown, iTickSize) {
17402         this.topConstraint = iUp;
17403         this.bottomConstraint = iDown;
17404
17405         this.minY = this.initPageY - iUp;
17406         this.maxY = this.initPageY + iDown;
17407         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17408
17409         this.constrainY = true;
17410
17411     },
17412
17413     /**
17414      * resetConstraints must be called if you manually reposition a dd element.
17415      * @method resetConstraints
17416      * @param {boolean} maintainOffset
17417      */
17418     resetConstraints: function() {
17419
17420
17421         // Maintain offsets if necessary
17422         if (this.initPageX || this.initPageX === 0) {
17423             // figure out how much this thing has moved
17424             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17425             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17426
17427             this.setInitPosition(dx, dy);
17428
17429         // This is the first time we have detected the element's position
17430         } else {
17431             this.setInitPosition();
17432         }
17433
17434         if (this.constrainX) {
17435             this.setXConstraint( this.leftConstraint,
17436                                  this.rightConstraint,
17437                                  this.xTickSize        );
17438         }
17439
17440         if (this.constrainY) {
17441             this.setYConstraint( this.topConstraint,
17442                                  this.bottomConstraint,
17443                                  this.yTickSize         );
17444         }
17445     },
17446
17447     /**
17448      * Normally the drag element is moved pixel by pixel, but we can specify
17449      * that it move a number of pixels at a time.  This method resolves the
17450      * location when we have it set up like this.
17451      * @method getTick
17452      * @param {int} val where we want to place the object
17453      * @param {int[]} tickArray sorted array of valid points
17454      * @return {int} the closest tick
17455      * @private
17456      */
17457     getTick: function(val, tickArray) {
17458
17459         if (!tickArray) {
17460             // If tick interval is not defined, it is effectively 1 pixel,
17461             // so we return the value passed to us.
17462             return val;
17463         } else if (tickArray[0] >= val) {
17464             // The value is lower than the first tick, so we return the first
17465             // tick.
17466             return tickArray[0];
17467         } else {
17468             for (var i=0, len=tickArray.length; i<len; ++i) {
17469                 var next = i + 1;
17470                 if (tickArray[next] && tickArray[next] >= val) {
17471                     var diff1 = val - tickArray[i];
17472                     var diff2 = tickArray[next] - val;
17473                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17474                 }
17475             }
17476
17477             // The value is larger than the last tick, so we return the last
17478             // tick.
17479             return tickArray[tickArray.length - 1];
17480         }
17481     },
17482
17483     /**
17484      * toString method
17485      * @method toString
17486      * @return {string} string representation of the dd obj
17487      */
17488     toString: function() {
17489         return ("DragDrop " + this.id);
17490     }
17491
17492 });
17493
17494 })();
17495 /*
17496  * Based on:
17497  * Ext JS Library 1.1.1
17498  * Copyright(c) 2006-2007, Ext JS, LLC.
17499  *
17500  * Originally Released Under LGPL - original licence link has changed is not relivant.
17501  *
17502  * Fork - LGPL
17503  * <script type="text/javascript">
17504  */
17505
17506
17507 /**
17508  * The drag and drop utility provides a framework for building drag and drop
17509  * applications.  In addition to enabling drag and drop for specific elements,
17510  * the drag and drop elements are tracked by the manager class, and the
17511  * interactions between the various elements are tracked during the drag and
17512  * the implementing code is notified about these important moments.
17513  */
17514
17515 // Only load the library once.  Rewriting the manager class would orphan
17516 // existing drag and drop instances.
17517 if (!Roo.dd.DragDropMgr) {
17518
17519 /**
17520  * @class Roo.dd.DragDropMgr
17521  * DragDropMgr is a singleton that tracks the element interaction for
17522  * all DragDrop items in the window.  Generally, you will not call
17523  * this class directly, but it does have helper methods that could
17524  * be useful in your DragDrop implementations.
17525  * @singleton
17526  */
17527 Roo.dd.DragDropMgr = function() {
17528
17529     var Event = Roo.EventManager;
17530
17531     return {
17532
17533         /**
17534          * Two dimensional Array of registered DragDrop objects.  The first
17535          * dimension is the DragDrop item group, the second the DragDrop
17536          * object.
17537          * @property ids
17538          * @type {string: string}
17539          * @private
17540          * @static
17541          */
17542         ids: {},
17543
17544         /**
17545          * Array of element ids defined as drag handles.  Used to determine
17546          * if the element that generated the mousedown event is actually the
17547          * handle and not the html element itself.
17548          * @property handleIds
17549          * @type {string: string}
17550          * @private
17551          * @static
17552          */
17553         handleIds: {},
17554
17555         /**
17556          * the DragDrop object that is currently being dragged
17557          * @property dragCurrent
17558          * @type DragDrop
17559          * @private
17560          * @static
17561          **/
17562         dragCurrent: null,
17563
17564         /**
17565          * the DragDrop object(s) that are being hovered over
17566          * @property dragOvers
17567          * @type Array
17568          * @private
17569          * @static
17570          */
17571         dragOvers: {},
17572
17573         /**
17574          * the X distance between the cursor and the object being dragged
17575          * @property deltaX
17576          * @type int
17577          * @private
17578          * @static
17579          */
17580         deltaX: 0,
17581
17582         /**
17583          * the Y distance between the cursor and the object being dragged
17584          * @property deltaY
17585          * @type int
17586          * @private
17587          * @static
17588          */
17589         deltaY: 0,
17590
17591         /**
17592          * Flag to determine if we should prevent the default behavior of the
17593          * events we define. By default this is true, but this can be set to
17594          * false if you need the default behavior (not recommended)
17595          * @property preventDefault
17596          * @type boolean
17597          * @static
17598          */
17599         preventDefault: true,
17600
17601         /**
17602          * Flag to determine if we should stop the propagation of the events
17603          * we generate. This is true by default but you may want to set it to
17604          * false if the html element contains other features that require the
17605          * mouse click.
17606          * @property stopPropagation
17607          * @type boolean
17608          * @static
17609          */
17610         stopPropagation: true,
17611
17612         /**
17613          * Internal flag that is set to true when drag and drop has been
17614          * intialized
17615          * @property initialized
17616          * @private
17617          * @static
17618          */
17619         initalized: false,
17620
17621         /**
17622          * All drag and drop can be disabled.
17623          * @property locked
17624          * @private
17625          * @static
17626          */
17627         locked: false,
17628
17629         /**
17630          * Called the first time an element is registered.
17631          * @method init
17632          * @private
17633          * @static
17634          */
17635         init: function() {
17636             this.initialized = true;
17637         },
17638
17639         /**
17640          * In point mode, drag and drop interaction is defined by the
17641          * location of the cursor during the drag/drop
17642          * @property POINT
17643          * @type int
17644          * @static
17645          */
17646         POINT: 0,
17647
17648         /**
17649          * In intersect mode, drag and drop interactio nis defined by the
17650          * overlap of two or more drag and drop objects.
17651          * @property INTERSECT
17652          * @type int
17653          * @static
17654          */
17655         INTERSECT: 1,
17656
17657         /**
17658          * The current drag and drop mode.  Default: POINT
17659          * @property mode
17660          * @type int
17661          * @static
17662          */
17663         mode: 0,
17664
17665         /**
17666          * Runs method on all drag and drop objects
17667          * @method _execOnAll
17668          * @private
17669          * @static
17670          */
17671         _execOnAll: function(sMethod, args) {
17672             for (var i in this.ids) {
17673                 for (var j in this.ids[i]) {
17674                     var oDD = this.ids[i][j];
17675                     if (! this.isTypeOfDD(oDD)) {
17676                         continue;
17677                     }
17678                     oDD[sMethod].apply(oDD, args);
17679                 }
17680             }
17681         },
17682
17683         /**
17684          * Drag and drop initialization.  Sets up the global event handlers
17685          * @method _onLoad
17686          * @private
17687          * @static
17688          */
17689         _onLoad: function() {
17690
17691             this.init();
17692
17693             if (!Roo.isTouch) {
17694                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17695                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17696             }
17697             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17698             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17699             
17700             Event.on(window,   "unload",    this._onUnload, this, true);
17701             Event.on(window,   "resize",    this._onResize, this, true);
17702             // Event.on(window,   "mouseout",    this._test);
17703
17704         },
17705
17706         /**
17707          * Reset constraints on all drag and drop objs
17708          * @method _onResize
17709          * @private
17710          * @static
17711          */
17712         _onResize: function(e) {
17713             this._execOnAll("resetConstraints", []);
17714         },
17715
17716         /**
17717          * Lock all drag and drop functionality
17718          * @method lock
17719          * @static
17720          */
17721         lock: function() { this.locked = true; },
17722
17723         /**
17724          * Unlock all drag and drop functionality
17725          * @method unlock
17726          * @static
17727          */
17728         unlock: function() { this.locked = false; },
17729
17730         /**
17731          * Is drag and drop locked?
17732          * @method isLocked
17733          * @return {boolean} True if drag and drop is locked, false otherwise.
17734          * @static
17735          */
17736         isLocked: function() { return this.locked; },
17737
17738         /**
17739          * Location cache that is set for all drag drop objects when a drag is
17740          * initiated, cleared when the drag is finished.
17741          * @property locationCache
17742          * @private
17743          * @static
17744          */
17745         locationCache: {},
17746
17747         /**
17748          * Set useCache to false if you want to force object the lookup of each
17749          * drag and drop linked element constantly during a drag.
17750          * @property useCache
17751          * @type boolean
17752          * @static
17753          */
17754         useCache: true,
17755
17756         /**
17757          * The number of pixels that the mouse needs to move after the
17758          * mousedown before the drag is initiated.  Default=3;
17759          * @property clickPixelThresh
17760          * @type int
17761          * @static
17762          */
17763         clickPixelThresh: 3,
17764
17765         /**
17766          * The number of milliseconds after the mousedown event to initiate the
17767          * drag if we don't get a mouseup event. Default=1000
17768          * @property clickTimeThresh
17769          * @type int
17770          * @static
17771          */
17772         clickTimeThresh: 350,
17773
17774         /**
17775          * Flag that indicates that either the drag pixel threshold or the
17776          * mousdown time threshold has been met
17777          * @property dragThreshMet
17778          * @type boolean
17779          * @private
17780          * @static
17781          */
17782         dragThreshMet: false,
17783
17784         /**
17785          * Timeout used for the click time threshold
17786          * @property clickTimeout
17787          * @type Object
17788          * @private
17789          * @static
17790          */
17791         clickTimeout: null,
17792
17793         /**
17794          * The X position of the mousedown event stored for later use when a
17795          * drag threshold is met.
17796          * @property startX
17797          * @type int
17798          * @private
17799          * @static
17800          */
17801         startX: 0,
17802
17803         /**
17804          * The Y position of the mousedown event stored for later use when a
17805          * drag threshold is met.
17806          * @property startY
17807          * @type int
17808          * @private
17809          * @static
17810          */
17811         startY: 0,
17812
17813         /**
17814          * Each DragDrop instance must be registered with the DragDropMgr.
17815          * This is executed in DragDrop.init()
17816          * @method regDragDrop
17817          * @param {DragDrop} oDD the DragDrop object to register
17818          * @param {String} sGroup the name of the group this element belongs to
17819          * @static
17820          */
17821         regDragDrop: function(oDD, sGroup) {
17822             if (!this.initialized) { this.init(); }
17823
17824             if (!this.ids[sGroup]) {
17825                 this.ids[sGroup] = {};
17826             }
17827             this.ids[sGroup][oDD.id] = oDD;
17828         },
17829
17830         /**
17831          * Removes the supplied dd instance from the supplied group. Executed
17832          * by DragDrop.removeFromGroup, so don't call this function directly.
17833          * @method removeDDFromGroup
17834          * @private
17835          * @static
17836          */
17837         removeDDFromGroup: function(oDD, sGroup) {
17838             if (!this.ids[sGroup]) {
17839                 this.ids[sGroup] = {};
17840             }
17841
17842             var obj = this.ids[sGroup];
17843             if (obj && obj[oDD.id]) {
17844                 delete obj[oDD.id];
17845             }
17846         },
17847
17848         /**
17849          * Unregisters a drag and drop item.  This is executed in
17850          * DragDrop.unreg, use that method instead of calling this directly.
17851          * @method _remove
17852          * @private
17853          * @static
17854          */
17855         _remove: function(oDD) {
17856             for (var g in oDD.groups) {
17857                 if (g && this.ids[g][oDD.id]) {
17858                     delete this.ids[g][oDD.id];
17859                 }
17860             }
17861             delete this.handleIds[oDD.id];
17862         },
17863
17864         /**
17865          * Each DragDrop handle element must be registered.  This is done
17866          * automatically when executing DragDrop.setHandleElId()
17867          * @method regHandle
17868          * @param {String} sDDId the DragDrop id this element is a handle for
17869          * @param {String} sHandleId the id of the element that is the drag
17870          * handle
17871          * @static
17872          */
17873         regHandle: function(sDDId, sHandleId) {
17874             if (!this.handleIds[sDDId]) {
17875                 this.handleIds[sDDId] = {};
17876             }
17877             this.handleIds[sDDId][sHandleId] = sHandleId;
17878         },
17879
17880         /**
17881          * Utility function to determine if a given element has been
17882          * registered as a drag drop item.
17883          * @method isDragDrop
17884          * @param {String} id the element id to check
17885          * @return {boolean} true if this element is a DragDrop item,
17886          * false otherwise
17887          * @static
17888          */
17889         isDragDrop: function(id) {
17890             return ( this.getDDById(id) ) ? true : false;
17891         },
17892
17893         /**
17894          * Returns the drag and drop instances that are in all groups the
17895          * passed in instance belongs to.
17896          * @method getRelated
17897          * @param {DragDrop} p_oDD the obj to get related data for
17898          * @param {boolean} bTargetsOnly if true, only return targetable objs
17899          * @return {DragDrop[]} the related instances
17900          * @static
17901          */
17902         getRelated: function(p_oDD, bTargetsOnly) {
17903             var oDDs = [];
17904             for (var i in p_oDD.groups) {
17905                 for (j in this.ids[i]) {
17906                     var dd = this.ids[i][j];
17907                     if (! this.isTypeOfDD(dd)) {
17908                         continue;
17909                     }
17910                     if (!bTargetsOnly || dd.isTarget) {
17911                         oDDs[oDDs.length] = dd;
17912                     }
17913                 }
17914             }
17915
17916             return oDDs;
17917         },
17918
17919         /**
17920          * Returns true if the specified dd target is a legal target for
17921          * the specifice drag obj
17922          * @method isLegalTarget
17923          * @param {DragDrop} the drag obj
17924          * @param {DragDrop} the target
17925          * @return {boolean} true if the target is a legal target for the
17926          * dd obj
17927          * @static
17928          */
17929         isLegalTarget: function (oDD, oTargetDD) {
17930             var targets = this.getRelated(oDD, true);
17931             for (var i=0, len=targets.length;i<len;++i) {
17932                 if (targets[i].id == oTargetDD.id) {
17933                     return true;
17934                 }
17935             }
17936
17937             return false;
17938         },
17939
17940         /**
17941          * My goal is to be able to transparently determine if an object is
17942          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17943          * returns "object", oDD.constructor.toString() always returns
17944          * "DragDrop" and not the name of the subclass.  So for now it just
17945          * evaluates a well-known variable in DragDrop.
17946          * @method isTypeOfDD
17947          * @param {Object} the object to evaluate
17948          * @return {boolean} true if typeof oDD = DragDrop
17949          * @static
17950          */
17951         isTypeOfDD: function (oDD) {
17952             return (oDD && oDD.__ygDragDrop);
17953         },
17954
17955         /**
17956          * Utility function to determine if a given element has been
17957          * registered as a drag drop handle for the given Drag Drop object.
17958          * @method isHandle
17959          * @param {String} id the element id to check
17960          * @return {boolean} true if this element is a DragDrop handle, false
17961          * otherwise
17962          * @static
17963          */
17964         isHandle: function(sDDId, sHandleId) {
17965             return ( this.handleIds[sDDId] &&
17966                             this.handleIds[sDDId][sHandleId] );
17967         },
17968
17969         /**
17970          * Returns the DragDrop instance for a given id
17971          * @method getDDById
17972          * @param {String} id the id of the DragDrop object
17973          * @return {DragDrop} the drag drop object, null if it is not found
17974          * @static
17975          */
17976         getDDById: function(id) {
17977             for (var i in this.ids) {
17978                 if (this.ids[i][id]) {
17979                     return this.ids[i][id];
17980                 }
17981             }
17982             return null;
17983         },
17984
17985         /**
17986          * Fired after a registered DragDrop object gets the mousedown event.
17987          * Sets up the events required to track the object being dragged
17988          * @method handleMouseDown
17989          * @param {Event} e the event
17990          * @param oDD the DragDrop object being dragged
17991          * @private
17992          * @static
17993          */
17994         handleMouseDown: function(e, oDD) {
17995             if(Roo.QuickTips){
17996                 Roo.QuickTips.disable();
17997             }
17998             this.currentTarget = e.getTarget();
17999
18000             this.dragCurrent = oDD;
18001
18002             var el = oDD.getEl();
18003
18004             // track start position
18005             this.startX = e.getPageX();
18006             this.startY = e.getPageY();
18007
18008             this.deltaX = this.startX - el.offsetLeft;
18009             this.deltaY = this.startY - el.offsetTop;
18010
18011             this.dragThreshMet = false;
18012
18013             this.clickTimeout = setTimeout(
18014                     function() {
18015                         var DDM = Roo.dd.DDM;
18016                         DDM.startDrag(DDM.startX, DDM.startY);
18017                     },
18018                     this.clickTimeThresh );
18019         },
18020
18021         /**
18022          * Fired when either the drag pixel threshol or the mousedown hold
18023          * time threshold has been met.
18024          * @method startDrag
18025          * @param x {int} the X position of the original mousedown
18026          * @param y {int} the Y position of the original mousedown
18027          * @static
18028          */
18029         startDrag: function(x, y) {
18030             clearTimeout(this.clickTimeout);
18031             if (this.dragCurrent) {
18032                 this.dragCurrent.b4StartDrag(x, y);
18033                 this.dragCurrent.startDrag(x, y);
18034             }
18035             this.dragThreshMet = true;
18036         },
18037
18038         /**
18039          * Internal function to handle the mouseup event.  Will be invoked
18040          * from the context of the document.
18041          * @method handleMouseUp
18042          * @param {Event} e the event
18043          * @private
18044          * @static
18045          */
18046         handleMouseUp: function(e) {
18047
18048             if(Roo.QuickTips){
18049                 Roo.QuickTips.enable();
18050             }
18051             if (! this.dragCurrent) {
18052                 return;
18053             }
18054
18055             clearTimeout(this.clickTimeout);
18056
18057             if (this.dragThreshMet) {
18058                 this.fireEvents(e, true);
18059             } else {
18060             }
18061
18062             this.stopDrag(e);
18063
18064             this.stopEvent(e);
18065         },
18066
18067         /**
18068          * Utility to stop event propagation and event default, if these
18069          * features are turned on.
18070          * @method stopEvent
18071          * @param {Event} e the event as returned by this.getEvent()
18072          * @static
18073          */
18074         stopEvent: function(e){
18075             if(this.stopPropagation) {
18076                 e.stopPropagation();
18077             }
18078
18079             if (this.preventDefault) {
18080                 e.preventDefault();
18081             }
18082         },
18083
18084         /**
18085          * Internal function to clean up event handlers after the drag
18086          * operation is complete
18087          * @method stopDrag
18088          * @param {Event} e the event
18089          * @private
18090          * @static
18091          */
18092         stopDrag: function(e) {
18093             // Fire the drag end event for the item that was dragged
18094             if (this.dragCurrent) {
18095                 if (this.dragThreshMet) {
18096                     this.dragCurrent.b4EndDrag(e);
18097                     this.dragCurrent.endDrag(e);
18098                 }
18099
18100                 this.dragCurrent.onMouseUp(e);
18101             }
18102
18103             this.dragCurrent = null;
18104             this.dragOvers = {};
18105         },
18106
18107         /**
18108          * Internal function to handle the mousemove event.  Will be invoked
18109          * from the context of the html element.
18110          *
18111          * @TODO figure out what we can do about mouse events lost when the
18112          * user drags objects beyond the window boundary.  Currently we can
18113          * detect this in internet explorer by verifying that the mouse is
18114          * down during the mousemove event.  Firefox doesn't give us the
18115          * button state on the mousemove event.
18116          * @method handleMouseMove
18117          * @param {Event} e the event
18118          * @private
18119          * @static
18120          */
18121         handleMouseMove: function(e) {
18122             if (! this.dragCurrent) {
18123                 return true;
18124             }
18125
18126             // var button = e.which || e.button;
18127
18128             // check for IE mouseup outside of page boundary
18129             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18130                 this.stopEvent(e);
18131                 return this.handleMouseUp(e);
18132             }
18133
18134             if (!this.dragThreshMet) {
18135                 var diffX = Math.abs(this.startX - e.getPageX());
18136                 var diffY = Math.abs(this.startY - e.getPageY());
18137                 if (diffX > this.clickPixelThresh ||
18138                             diffY > this.clickPixelThresh) {
18139                     this.startDrag(this.startX, this.startY);
18140                 }
18141             }
18142
18143             if (this.dragThreshMet) {
18144                 this.dragCurrent.b4Drag(e);
18145                 this.dragCurrent.onDrag(e);
18146                 if(!this.dragCurrent.moveOnly){
18147                     this.fireEvents(e, false);
18148                 }
18149             }
18150
18151             this.stopEvent(e);
18152
18153             return true;
18154         },
18155
18156         /**
18157          * Iterates over all of the DragDrop elements to find ones we are
18158          * hovering over or dropping on
18159          * @method fireEvents
18160          * @param {Event} e the event
18161          * @param {boolean} isDrop is this a drop op or a mouseover op?
18162          * @private
18163          * @static
18164          */
18165         fireEvents: function(e, isDrop) {
18166             var dc = this.dragCurrent;
18167
18168             // If the user did the mouse up outside of the window, we could
18169             // get here even though we have ended the drag.
18170             if (!dc || dc.isLocked()) {
18171                 return;
18172             }
18173
18174             var pt = e.getPoint();
18175
18176             // cache the previous dragOver array
18177             var oldOvers = [];
18178
18179             var outEvts   = [];
18180             var overEvts  = [];
18181             var dropEvts  = [];
18182             var enterEvts = [];
18183
18184             // Check to see if the object(s) we were hovering over is no longer
18185             // being hovered over so we can fire the onDragOut event
18186             for (var i in this.dragOvers) {
18187
18188                 var ddo = this.dragOvers[i];
18189
18190                 if (! this.isTypeOfDD(ddo)) {
18191                     continue;
18192                 }
18193
18194                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18195                     outEvts.push( ddo );
18196                 }
18197
18198                 oldOvers[i] = true;
18199                 delete this.dragOvers[i];
18200             }
18201
18202             for (var sGroup in dc.groups) {
18203
18204                 if ("string" != typeof sGroup) {
18205                     continue;
18206                 }
18207
18208                 for (i in this.ids[sGroup]) {
18209                     var oDD = this.ids[sGroup][i];
18210                     if (! this.isTypeOfDD(oDD)) {
18211                         continue;
18212                     }
18213
18214                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18215                         if (this.isOverTarget(pt, oDD, this.mode)) {
18216                             // look for drop interactions
18217                             if (isDrop) {
18218                                 dropEvts.push( oDD );
18219                             // look for drag enter and drag over interactions
18220                             } else {
18221
18222                                 // initial drag over: dragEnter fires
18223                                 if (!oldOvers[oDD.id]) {
18224                                     enterEvts.push( oDD );
18225                                 // subsequent drag overs: dragOver fires
18226                                 } else {
18227                                     overEvts.push( oDD );
18228                                 }
18229
18230                                 this.dragOvers[oDD.id] = oDD;
18231                             }
18232                         }
18233                     }
18234                 }
18235             }
18236
18237             if (this.mode) {
18238                 if (outEvts.length) {
18239                     dc.b4DragOut(e, outEvts);
18240                     dc.onDragOut(e, outEvts);
18241                 }
18242
18243                 if (enterEvts.length) {
18244                     dc.onDragEnter(e, enterEvts);
18245                 }
18246
18247                 if (overEvts.length) {
18248                     dc.b4DragOver(e, overEvts);
18249                     dc.onDragOver(e, overEvts);
18250                 }
18251
18252                 if (dropEvts.length) {
18253                     dc.b4DragDrop(e, dropEvts);
18254                     dc.onDragDrop(e, dropEvts);
18255                 }
18256
18257             } else {
18258                 // fire dragout events
18259                 var len = 0;
18260                 for (i=0, len=outEvts.length; i<len; ++i) {
18261                     dc.b4DragOut(e, outEvts[i].id);
18262                     dc.onDragOut(e, outEvts[i].id);
18263                 }
18264
18265                 // fire enter events
18266                 for (i=0,len=enterEvts.length; i<len; ++i) {
18267                     // dc.b4DragEnter(e, oDD.id);
18268                     dc.onDragEnter(e, enterEvts[i].id);
18269                 }
18270
18271                 // fire over events
18272                 for (i=0,len=overEvts.length; i<len; ++i) {
18273                     dc.b4DragOver(e, overEvts[i].id);
18274                     dc.onDragOver(e, overEvts[i].id);
18275                 }
18276
18277                 // fire drop events
18278                 for (i=0, len=dropEvts.length; i<len; ++i) {
18279                     dc.b4DragDrop(e, dropEvts[i].id);
18280                     dc.onDragDrop(e, dropEvts[i].id);
18281                 }
18282
18283             }
18284
18285             // notify about a drop that did not find a target
18286             if (isDrop && !dropEvts.length) {
18287                 dc.onInvalidDrop(e);
18288             }
18289
18290         },
18291
18292         /**
18293          * Helper function for getting the best match from the list of drag
18294          * and drop objects returned by the drag and drop events when we are
18295          * in INTERSECT mode.  It returns either the first object that the
18296          * cursor is over, or the object that has the greatest overlap with
18297          * the dragged element.
18298          * @method getBestMatch
18299          * @param  {DragDrop[]} dds The array of drag and drop objects
18300          * targeted
18301          * @return {DragDrop}       The best single match
18302          * @static
18303          */
18304         getBestMatch: function(dds) {
18305             var winner = null;
18306             // Return null if the input is not what we expect
18307             //if (!dds || !dds.length || dds.length == 0) {
18308                // winner = null;
18309             // If there is only one item, it wins
18310             //} else if (dds.length == 1) {
18311
18312             var len = dds.length;
18313
18314             if (len == 1) {
18315                 winner = dds[0];
18316             } else {
18317                 // Loop through the targeted items
18318                 for (var i=0; i<len; ++i) {
18319                     var dd = dds[i];
18320                     // If the cursor is over the object, it wins.  If the
18321                     // cursor is over multiple matches, the first one we come
18322                     // to wins.
18323                     if (dd.cursorIsOver) {
18324                         winner = dd;
18325                         break;
18326                     // Otherwise the object with the most overlap wins
18327                     } else {
18328                         if (!winner ||
18329                             winner.overlap.getArea() < dd.overlap.getArea()) {
18330                             winner = dd;
18331                         }
18332                     }
18333                 }
18334             }
18335
18336             return winner;
18337         },
18338
18339         /**
18340          * Refreshes the cache of the top-left and bottom-right points of the
18341          * drag and drop objects in the specified group(s).  This is in the
18342          * format that is stored in the drag and drop instance, so typical
18343          * usage is:
18344          * <code>
18345          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18346          * </code>
18347          * Alternatively:
18348          * <code>
18349          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18350          * </code>
18351          * @TODO this really should be an indexed array.  Alternatively this
18352          * method could accept both.
18353          * @method refreshCache
18354          * @param {Object} groups an associative array of groups to refresh
18355          * @static
18356          */
18357         refreshCache: function(groups) {
18358             for (var sGroup in groups) {
18359                 if ("string" != typeof sGroup) {
18360                     continue;
18361                 }
18362                 for (var i in this.ids[sGroup]) {
18363                     var oDD = this.ids[sGroup][i];
18364
18365                     if (this.isTypeOfDD(oDD)) {
18366                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18367                         var loc = this.getLocation(oDD);
18368                         if (loc) {
18369                             this.locationCache[oDD.id] = loc;
18370                         } else {
18371                             delete this.locationCache[oDD.id];
18372                             // this will unregister the drag and drop object if
18373                             // the element is not in a usable state
18374                             // oDD.unreg();
18375                         }
18376                     }
18377                 }
18378             }
18379         },
18380
18381         /**
18382          * This checks to make sure an element exists and is in the DOM.  The
18383          * main purpose is to handle cases where innerHTML is used to remove
18384          * drag and drop objects from the DOM.  IE provides an 'unspecified
18385          * error' when trying to access the offsetParent of such an element
18386          * @method verifyEl
18387          * @param {HTMLElement} el the element to check
18388          * @return {boolean} true if the element looks usable
18389          * @static
18390          */
18391         verifyEl: function(el) {
18392             if (el) {
18393                 var parent;
18394                 if(Roo.isIE){
18395                     try{
18396                         parent = el.offsetParent;
18397                     }catch(e){}
18398                 }else{
18399                     parent = el.offsetParent;
18400                 }
18401                 if (parent) {
18402                     return true;
18403                 }
18404             }
18405
18406             return false;
18407         },
18408
18409         /**
18410          * Returns a Region object containing the drag and drop element's position
18411          * and size, including the padding configured for it
18412          * @method getLocation
18413          * @param {DragDrop} oDD the drag and drop object to get the
18414          *                       location for
18415          * @return {Roo.lib.Region} a Region object representing the total area
18416          *                             the element occupies, including any padding
18417          *                             the instance is configured for.
18418          * @static
18419          */
18420         getLocation: function(oDD) {
18421             if (! this.isTypeOfDD(oDD)) {
18422                 return null;
18423             }
18424
18425             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18426
18427             try {
18428                 pos= Roo.lib.Dom.getXY(el);
18429             } catch (e) { }
18430
18431             if (!pos) {
18432                 return null;
18433             }
18434
18435             x1 = pos[0];
18436             x2 = x1 + el.offsetWidth;
18437             y1 = pos[1];
18438             y2 = y1 + el.offsetHeight;
18439
18440             t = y1 - oDD.padding[0];
18441             r = x2 + oDD.padding[1];
18442             b = y2 + oDD.padding[2];
18443             l = x1 - oDD.padding[3];
18444
18445             return new Roo.lib.Region( t, r, b, l );
18446         },
18447
18448         /**
18449          * Checks the cursor location to see if it over the target
18450          * @method isOverTarget
18451          * @param {Roo.lib.Point} pt The point to evaluate
18452          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18453          * @return {boolean} true if the mouse is over the target
18454          * @private
18455          * @static
18456          */
18457         isOverTarget: function(pt, oTarget, intersect) {
18458             // use cache if available
18459             var loc = this.locationCache[oTarget.id];
18460             if (!loc || !this.useCache) {
18461                 loc = this.getLocation(oTarget);
18462                 this.locationCache[oTarget.id] = loc;
18463
18464             }
18465
18466             if (!loc) {
18467                 return false;
18468             }
18469
18470             oTarget.cursorIsOver = loc.contains( pt );
18471
18472             // DragDrop is using this as a sanity check for the initial mousedown
18473             // in this case we are done.  In POINT mode, if the drag obj has no
18474             // contraints, we are also done. Otherwise we need to evaluate the
18475             // location of the target as related to the actual location of the
18476             // dragged element.
18477             var dc = this.dragCurrent;
18478             if (!dc || !dc.getTargetCoord ||
18479                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18480                 return oTarget.cursorIsOver;
18481             }
18482
18483             oTarget.overlap = null;
18484
18485             // Get the current location of the drag element, this is the
18486             // location of the mouse event less the delta that represents
18487             // where the original mousedown happened on the element.  We
18488             // need to consider constraints and ticks as well.
18489             var pos = dc.getTargetCoord(pt.x, pt.y);
18490
18491             var el = dc.getDragEl();
18492             var curRegion = new Roo.lib.Region( pos.y,
18493                                                    pos.x + el.offsetWidth,
18494                                                    pos.y + el.offsetHeight,
18495                                                    pos.x );
18496
18497             var overlap = curRegion.intersect(loc);
18498
18499             if (overlap) {
18500                 oTarget.overlap = overlap;
18501                 return (intersect) ? true : oTarget.cursorIsOver;
18502             } else {
18503                 return false;
18504             }
18505         },
18506
18507         /**
18508          * unload event handler
18509          * @method _onUnload
18510          * @private
18511          * @static
18512          */
18513         _onUnload: function(e, me) {
18514             Roo.dd.DragDropMgr.unregAll();
18515         },
18516
18517         /**
18518          * Cleans up the drag and drop events and objects.
18519          * @method unregAll
18520          * @private
18521          * @static
18522          */
18523         unregAll: function() {
18524
18525             if (this.dragCurrent) {
18526                 this.stopDrag();
18527                 this.dragCurrent = null;
18528             }
18529
18530             this._execOnAll("unreg", []);
18531
18532             for (i in this.elementCache) {
18533                 delete this.elementCache[i];
18534             }
18535
18536             this.elementCache = {};
18537             this.ids = {};
18538         },
18539
18540         /**
18541          * A cache of DOM elements
18542          * @property elementCache
18543          * @private
18544          * @static
18545          */
18546         elementCache: {},
18547
18548         /**
18549          * Get the wrapper for the DOM element specified
18550          * @method getElWrapper
18551          * @param {String} id the id of the element to get
18552          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18553          * @private
18554          * @deprecated This wrapper isn't that useful
18555          * @static
18556          */
18557         getElWrapper: function(id) {
18558             var oWrapper = this.elementCache[id];
18559             if (!oWrapper || !oWrapper.el) {
18560                 oWrapper = this.elementCache[id] =
18561                     new this.ElementWrapper(Roo.getDom(id));
18562             }
18563             return oWrapper;
18564         },
18565
18566         /**
18567          * Returns the actual DOM element
18568          * @method getElement
18569          * @param {String} id the id of the elment to get
18570          * @return {Object} The element
18571          * @deprecated use Roo.getDom instead
18572          * @static
18573          */
18574         getElement: function(id) {
18575             return Roo.getDom(id);
18576         },
18577
18578         /**
18579          * Returns the style property for the DOM element (i.e.,
18580          * document.getElById(id).style)
18581          * @method getCss
18582          * @param {String} id the id of the elment to get
18583          * @return {Object} The style property of the element
18584          * @deprecated use Roo.getDom instead
18585          * @static
18586          */
18587         getCss: function(id) {
18588             var el = Roo.getDom(id);
18589             return (el) ? el.style : null;
18590         },
18591
18592         /**
18593          * Inner class for cached elements
18594          * @class DragDropMgr.ElementWrapper
18595          * @for DragDropMgr
18596          * @private
18597          * @deprecated
18598          */
18599         ElementWrapper: function(el) {
18600                 /**
18601                  * The element
18602                  * @property el
18603                  */
18604                 this.el = el || null;
18605                 /**
18606                  * The element id
18607                  * @property id
18608                  */
18609                 this.id = this.el && el.id;
18610                 /**
18611                  * A reference to the style property
18612                  * @property css
18613                  */
18614                 this.css = this.el && el.style;
18615             },
18616
18617         /**
18618          * Returns the X position of an html element
18619          * @method getPosX
18620          * @param el the element for which to get the position
18621          * @return {int} the X coordinate
18622          * @for DragDropMgr
18623          * @deprecated use Roo.lib.Dom.getX instead
18624          * @static
18625          */
18626         getPosX: function(el) {
18627             return Roo.lib.Dom.getX(el);
18628         },
18629
18630         /**
18631          * Returns the Y position of an html element
18632          * @method getPosY
18633          * @param el the element for which to get the position
18634          * @return {int} the Y coordinate
18635          * @deprecated use Roo.lib.Dom.getY instead
18636          * @static
18637          */
18638         getPosY: function(el) {
18639             return Roo.lib.Dom.getY(el);
18640         },
18641
18642         /**
18643          * Swap two nodes.  In IE, we use the native method, for others we
18644          * emulate the IE behavior
18645          * @method swapNode
18646          * @param n1 the first node to swap
18647          * @param n2 the other node to swap
18648          * @static
18649          */
18650         swapNode: function(n1, n2) {
18651             if (n1.swapNode) {
18652                 n1.swapNode(n2);
18653             } else {
18654                 var p = n2.parentNode;
18655                 var s = n2.nextSibling;
18656
18657                 if (s == n1) {
18658                     p.insertBefore(n1, n2);
18659                 } else if (n2 == n1.nextSibling) {
18660                     p.insertBefore(n2, n1);
18661                 } else {
18662                     n1.parentNode.replaceChild(n2, n1);
18663                     p.insertBefore(n1, s);
18664                 }
18665             }
18666         },
18667
18668         /**
18669          * Returns the current scroll position
18670          * @method getScroll
18671          * @private
18672          * @static
18673          */
18674         getScroll: function () {
18675             var t, l, dde=document.documentElement, db=document.body;
18676             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18677                 t = dde.scrollTop;
18678                 l = dde.scrollLeft;
18679             } else if (db) {
18680                 t = db.scrollTop;
18681                 l = db.scrollLeft;
18682             } else {
18683
18684             }
18685             return { top: t, left: l };
18686         },
18687
18688         /**
18689          * Returns the specified element style property
18690          * @method getStyle
18691          * @param {HTMLElement} el          the element
18692          * @param {string}      styleProp   the style property
18693          * @return {string} The value of the style property
18694          * @deprecated use Roo.lib.Dom.getStyle
18695          * @static
18696          */
18697         getStyle: function(el, styleProp) {
18698             return Roo.fly(el).getStyle(styleProp);
18699         },
18700
18701         /**
18702          * Gets the scrollTop
18703          * @method getScrollTop
18704          * @return {int} the document's scrollTop
18705          * @static
18706          */
18707         getScrollTop: function () { return this.getScroll().top; },
18708
18709         /**
18710          * Gets the scrollLeft
18711          * @method getScrollLeft
18712          * @return {int} the document's scrollTop
18713          * @static
18714          */
18715         getScrollLeft: function () { return this.getScroll().left; },
18716
18717         /**
18718          * Sets the x/y position of an element to the location of the
18719          * target element.
18720          * @method moveToEl
18721          * @param {HTMLElement} moveEl      The element to move
18722          * @param {HTMLElement} targetEl    The position reference element
18723          * @static
18724          */
18725         moveToEl: function (moveEl, targetEl) {
18726             var aCoord = Roo.lib.Dom.getXY(targetEl);
18727             Roo.lib.Dom.setXY(moveEl, aCoord);
18728         },
18729
18730         /**
18731          * Numeric array sort function
18732          * @method numericSort
18733          * @static
18734          */
18735         numericSort: function(a, b) { return (a - b); },
18736
18737         /**
18738          * Internal counter
18739          * @property _timeoutCount
18740          * @private
18741          * @static
18742          */
18743         _timeoutCount: 0,
18744
18745         /**
18746          * Trying to make the load order less important.  Without this we get
18747          * an error if this file is loaded before the Event Utility.
18748          * @method _addListeners
18749          * @private
18750          * @static
18751          */
18752         _addListeners: function() {
18753             var DDM = Roo.dd.DDM;
18754             if ( Roo.lib.Event && document ) {
18755                 DDM._onLoad();
18756             } else {
18757                 if (DDM._timeoutCount > 2000) {
18758                 } else {
18759                     setTimeout(DDM._addListeners, 10);
18760                     if (document && document.body) {
18761                         DDM._timeoutCount += 1;
18762                     }
18763                 }
18764             }
18765         },
18766
18767         /**
18768          * Recursively searches the immediate parent and all child nodes for
18769          * the handle element in order to determine wheter or not it was
18770          * clicked.
18771          * @method handleWasClicked
18772          * @param node the html element to inspect
18773          * @static
18774          */
18775         handleWasClicked: function(node, id) {
18776             if (this.isHandle(id, node.id)) {
18777                 return true;
18778             } else {
18779                 // check to see if this is a text node child of the one we want
18780                 var p = node.parentNode;
18781
18782                 while (p) {
18783                     if (this.isHandle(id, p.id)) {
18784                         return true;
18785                     } else {
18786                         p = p.parentNode;
18787                     }
18788                 }
18789             }
18790
18791             return false;
18792         }
18793
18794     };
18795
18796 }();
18797
18798 // shorter alias, save a few bytes
18799 Roo.dd.DDM = Roo.dd.DragDropMgr;
18800 Roo.dd.DDM._addListeners();
18801
18802 }/*
18803  * Based on:
18804  * Ext JS Library 1.1.1
18805  * Copyright(c) 2006-2007, Ext JS, LLC.
18806  *
18807  * Originally Released Under LGPL - original licence link has changed is not relivant.
18808  *
18809  * Fork - LGPL
18810  * <script type="text/javascript">
18811  */
18812
18813 /**
18814  * @class Roo.dd.DD
18815  * A DragDrop implementation where the linked element follows the
18816  * mouse cursor during a drag.
18817  * @extends Roo.dd.DragDrop
18818  * @constructor
18819  * @param {String} id the id of the linked element
18820  * @param {String} sGroup the group of related DragDrop items
18821  * @param {object} config an object containing configurable attributes
18822  *                Valid properties for DD:
18823  *                    scroll
18824  */
18825 Roo.dd.DD = function(id, sGroup, config) {
18826     if (id) {
18827         this.init(id, sGroup, config);
18828     }
18829 };
18830
18831 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18832
18833     /**
18834      * When set to true, the utility automatically tries to scroll the browser
18835      * window wehn a drag and drop element is dragged near the viewport boundary.
18836      * Defaults to true.
18837      * @property scroll
18838      * @type boolean
18839      */
18840     scroll: true,
18841
18842     /**
18843      * Sets the pointer offset to the distance between the linked element's top
18844      * left corner and the location the element was clicked
18845      * @method autoOffset
18846      * @param {int} iPageX the X coordinate of the click
18847      * @param {int} iPageY the Y coordinate of the click
18848      */
18849     autoOffset: function(iPageX, iPageY) {
18850         var x = iPageX - this.startPageX;
18851         var y = iPageY - this.startPageY;
18852         this.setDelta(x, y);
18853     },
18854
18855     /**
18856      * Sets the pointer offset.  You can call this directly to force the
18857      * offset to be in a particular location (e.g., pass in 0,0 to set it
18858      * to the center of the object)
18859      * @method setDelta
18860      * @param {int} iDeltaX the distance from the left
18861      * @param {int} iDeltaY the distance from the top
18862      */
18863     setDelta: function(iDeltaX, iDeltaY) {
18864         this.deltaX = iDeltaX;
18865         this.deltaY = iDeltaY;
18866     },
18867
18868     /**
18869      * Sets the drag element to the location of the mousedown or click event,
18870      * maintaining the cursor location relative to the location on the element
18871      * that was clicked.  Override this if you want to place the element in a
18872      * location other than where the cursor is.
18873      * @method setDragElPos
18874      * @param {int} iPageX the X coordinate of the mousedown or drag event
18875      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18876      */
18877     setDragElPos: function(iPageX, iPageY) {
18878         // the first time we do this, we are going to check to make sure
18879         // the element has css positioning
18880
18881         var el = this.getDragEl();
18882         this.alignElWithMouse(el, iPageX, iPageY);
18883     },
18884
18885     /**
18886      * Sets the element to the location of the mousedown or click event,
18887      * maintaining the cursor location relative to the location on the element
18888      * that was clicked.  Override this if you want to place the element in a
18889      * location other than where the cursor is.
18890      * @method alignElWithMouse
18891      * @param {HTMLElement} el the element to move
18892      * @param {int} iPageX the X coordinate of the mousedown or drag event
18893      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18894      */
18895     alignElWithMouse: function(el, iPageX, iPageY) {
18896         var oCoord = this.getTargetCoord(iPageX, iPageY);
18897         var fly = el.dom ? el : Roo.fly(el);
18898         if (!this.deltaSetXY) {
18899             var aCoord = [oCoord.x, oCoord.y];
18900             fly.setXY(aCoord);
18901             var newLeft = fly.getLeft(true);
18902             var newTop  = fly.getTop(true);
18903             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18904         } else {
18905             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18906         }
18907
18908         this.cachePosition(oCoord.x, oCoord.y);
18909         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18910         return oCoord;
18911     },
18912
18913     /**
18914      * Saves the most recent position so that we can reset the constraints and
18915      * tick marks on-demand.  We need to know this so that we can calculate the
18916      * number of pixels the element is offset from its original position.
18917      * @method cachePosition
18918      * @param iPageX the current x position (optional, this just makes it so we
18919      * don't have to look it up again)
18920      * @param iPageY the current y position (optional, this just makes it so we
18921      * don't have to look it up again)
18922      */
18923     cachePosition: function(iPageX, iPageY) {
18924         if (iPageX) {
18925             this.lastPageX = iPageX;
18926             this.lastPageY = iPageY;
18927         } else {
18928             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18929             this.lastPageX = aCoord[0];
18930             this.lastPageY = aCoord[1];
18931         }
18932     },
18933
18934     /**
18935      * Auto-scroll the window if the dragged object has been moved beyond the
18936      * visible window boundary.
18937      * @method autoScroll
18938      * @param {int} x the drag element's x position
18939      * @param {int} y the drag element's y position
18940      * @param {int} h the height of the drag element
18941      * @param {int} w the width of the drag element
18942      * @private
18943      */
18944     autoScroll: function(x, y, h, w) {
18945
18946         if (this.scroll) {
18947             // The client height
18948             var clientH = Roo.lib.Dom.getViewWidth();
18949
18950             // The client width
18951             var clientW = Roo.lib.Dom.getViewHeight();
18952
18953             // The amt scrolled down
18954             var st = this.DDM.getScrollTop();
18955
18956             // The amt scrolled right
18957             var sl = this.DDM.getScrollLeft();
18958
18959             // Location of the bottom of the element
18960             var bot = h + y;
18961
18962             // Location of the right of the element
18963             var right = w + x;
18964
18965             // The distance from the cursor to the bottom of the visible area,
18966             // adjusted so that we don't scroll if the cursor is beyond the
18967             // element drag constraints
18968             var toBot = (clientH + st - y - this.deltaY);
18969
18970             // The distance from the cursor to the right of the visible area
18971             var toRight = (clientW + sl - x - this.deltaX);
18972
18973
18974             // How close to the edge the cursor must be before we scroll
18975             // var thresh = (document.all) ? 100 : 40;
18976             var thresh = 40;
18977
18978             // How many pixels to scroll per autoscroll op.  This helps to reduce
18979             // clunky scrolling. IE is more sensitive about this ... it needs this
18980             // value to be higher.
18981             var scrAmt = (document.all) ? 80 : 30;
18982
18983             // Scroll down if we are near the bottom of the visible page and the
18984             // obj extends below the crease
18985             if ( bot > clientH && toBot < thresh ) {
18986                 window.scrollTo(sl, st + scrAmt);
18987             }
18988
18989             // Scroll up if the window is scrolled down and the top of the object
18990             // goes above the top border
18991             if ( y < st && st > 0 && y - st < thresh ) {
18992                 window.scrollTo(sl, st - scrAmt);
18993             }
18994
18995             // Scroll right if the obj is beyond the right border and the cursor is
18996             // near the border.
18997             if ( right > clientW && toRight < thresh ) {
18998                 window.scrollTo(sl + scrAmt, st);
18999             }
19000
19001             // Scroll left if the window has been scrolled to the right and the obj
19002             // extends past the left border
19003             if ( x < sl && sl > 0 && x - sl < thresh ) {
19004                 window.scrollTo(sl - scrAmt, st);
19005             }
19006         }
19007     },
19008
19009     /**
19010      * Finds the location the element should be placed if we want to move
19011      * it to where the mouse location less the click offset would place us.
19012      * @method getTargetCoord
19013      * @param {int} iPageX the X coordinate of the click
19014      * @param {int} iPageY the Y coordinate of the click
19015      * @return an object that contains the coordinates (Object.x and Object.y)
19016      * @private
19017      */
19018     getTargetCoord: function(iPageX, iPageY) {
19019
19020
19021         var x = iPageX - this.deltaX;
19022         var y = iPageY - this.deltaY;
19023
19024         if (this.constrainX) {
19025             if (x < this.minX) { x = this.minX; }
19026             if (x > this.maxX) { x = this.maxX; }
19027         }
19028
19029         if (this.constrainY) {
19030             if (y < this.minY) { y = this.minY; }
19031             if (y > this.maxY) { y = this.maxY; }
19032         }
19033
19034         x = this.getTick(x, this.xTicks);
19035         y = this.getTick(y, this.yTicks);
19036
19037
19038         return {x:x, y:y};
19039     },
19040
19041     /*
19042      * Sets up config options specific to this class. Overrides
19043      * Roo.dd.DragDrop, but all versions of this method through the
19044      * inheritance chain are called
19045      */
19046     applyConfig: function() {
19047         Roo.dd.DD.superclass.applyConfig.call(this);
19048         this.scroll = (this.config.scroll !== false);
19049     },
19050
19051     /*
19052      * Event that fires prior to the onMouseDown event.  Overrides
19053      * Roo.dd.DragDrop.
19054      */
19055     b4MouseDown: function(e) {
19056         // this.resetConstraints();
19057         this.autoOffset(e.getPageX(),
19058                             e.getPageY());
19059     },
19060
19061     /*
19062      * Event that fires prior to the onDrag event.  Overrides
19063      * Roo.dd.DragDrop.
19064      */
19065     b4Drag: function(e) {
19066         this.setDragElPos(e.getPageX(),
19067                             e.getPageY());
19068     },
19069
19070     toString: function() {
19071         return ("DD " + this.id);
19072     }
19073
19074     //////////////////////////////////////////////////////////////////////////
19075     // Debugging ygDragDrop events that can be overridden
19076     //////////////////////////////////////////////////////////////////////////
19077     /*
19078     startDrag: function(x, y) {
19079     },
19080
19081     onDrag: function(e) {
19082     },
19083
19084     onDragEnter: function(e, id) {
19085     },
19086
19087     onDragOver: function(e, id) {
19088     },
19089
19090     onDragOut: function(e, id) {
19091     },
19092
19093     onDragDrop: function(e, id) {
19094     },
19095
19096     endDrag: function(e) {
19097     }
19098
19099     */
19100
19101 });/*
19102  * Based on:
19103  * Ext JS Library 1.1.1
19104  * Copyright(c) 2006-2007, Ext JS, LLC.
19105  *
19106  * Originally Released Under LGPL - original licence link has changed is not relivant.
19107  *
19108  * Fork - LGPL
19109  * <script type="text/javascript">
19110  */
19111
19112 /**
19113  * @class Roo.dd.DDProxy
19114  * A DragDrop implementation that inserts an empty, bordered div into
19115  * the document that follows the cursor during drag operations.  At the time of
19116  * the click, the frame div is resized to the dimensions of the linked html
19117  * element, and moved to the exact location of the linked element.
19118  *
19119  * References to the "frame" element refer to the single proxy element that
19120  * was created to be dragged in place of all DDProxy elements on the
19121  * page.
19122  *
19123  * @extends Roo.dd.DD
19124  * @constructor
19125  * @param {String} id the id of the linked html element
19126  * @param {String} sGroup the group of related DragDrop objects
19127  * @param {object} config an object containing configurable attributes
19128  *                Valid properties for DDProxy in addition to those in DragDrop:
19129  *                   resizeFrame, centerFrame, dragElId
19130  */
19131 Roo.dd.DDProxy = function(id, sGroup, config) {
19132     if (id) {
19133         this.init(id, sGroup, config);
19134         this.initFrame();
19135     }
19136 };
19137
19138 /**
19139  * The default drag frame div id
19140  * @property Roo.dd.DDProxy.dragElId
19141  * @type String
19142  * @static
19143  */
19144 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19145
19146 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19147
19148     /**
19149      * By default we resize the drag frame to be the same size as the element
19150      * we want to drag (this is to get the frame effect).  We can turn it off
19151      * if we want a different behavior.
19152      * @property resizeFrame
19153      * @type boolean
19154      */
19155     resizeFrame: true,
19156
19157     /**
19158      * By default the frame is positioned exactly where the drag element is, so
19159      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19160      * you do not have constraints on the obj is to have the drag frame centered
19161      * around the cursor.  Set centerFrame to true for this effect.
19162      * @property centerFrame
19163      * @type boolean
19164      */
19165     centerFrame: false,
19166
19167     /**
19168      * Creates the proxy element if it does not yet exist
19169      * @method createFrame
19170      */
19171     createFrame: function() {
19172         var self = this;
19173         var body = document.body;
19174
19175         if (!body || !body.firstChild) {
19176             setTimeout( function() { self.createFrame(); }, 50 );
19177             return;
19178         }
19179
19180         var div = this.getDragEl();
19181
19182         if (!div) {
19183             div    = document.createElement("div");
19184             div.id = this.dragElId;
19185             var s  = div.style;
19186
19187             s.position   = "absolute";
19188             s.visibility = "hidden";
19189             s.cursor     = "move";
19190             s.border     = "2px solid #aaa";
19191             s.zIndex     = 999;
19192
19193             // appendChild can blow up IE if invoked prior to the window load event
19194             // while rendering a table.  It is possible there are other scenarios
19195             // that would cause this to happen as well.
19196             body.insertBefore(div, body.firstChild);
19197         }
19198     },
19199
19200     /**
19201      * Initialization for the drag frame element.  Must be called in the
19202      * constructor of all subclasses
19203      * @method initFrame
19204      */
19205     initFrame: function() {
19206         this.createFrame();
19207     },
19208
19209     applyConfig: function() {
19210         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19211
19212         this.resizeFrame = (this.config.resizeFrame !== false);
19213         this.centerFrame = (this.config.centerFrame);
19214         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19215     },
19216
19217     /**
19218      * Resizes the drag frame to the dimensions of the clicked object, positions
19219      * it over the object, and finally displays it
19220      * @method showFrame
19221      * @param {int} iPageX X click position
19222      * @param {int} iPageY Y click position
19223      * @private
19224      */
19225     showFrame: function(iPageX, iPageY) {
19226         var el = this.getEl();
19227         var dragEl = this.getDragEl();
19228         var s = dragEl.style;
19229
19230         this._resizeProxy();
19231
19232         if (this.centerFrame) {
19233             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19234                            Math.round(parseInt(s.height, 10)/2) );
19235         }
19236
19237         this.setDragElPos(iPageX, iPageY);
19238
19239         Roo.fly(dragEl).show();
19240     },
19241
19242     /**
19243      * The proxy is automatically resized to the dimensions of the linked
19244      * element when a drag is initiated, unless resizeFrame is set to false
19245      * @method _resizeProxy
19246      * @private
19247      */
19248     _resizeProxy: function() {
19249         if (this.resizeFrame) {
19250             var el = this.getEl();
19251             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19252         }
19253     },
19254
19255     // overrides Roo.dd.DragDrop
19256     b4MouseDown: function(e) {
19257         var x = e.getPageX();
19258         var y = e.getPageY();
19259         this.autoOffset(x, y);
19260         this.setDragElPos(x, y);
19261     },
19262
19263     // overrides Roo.dd.DragDrop
19264     b4StartDrag: function(x, y) {
19265         // show the drag frame
19266         this.showFrame(x, y);
19267     },
19268
19269     // overrides Roo.dd.DragDrop
19270     b4EndDrag: function(e) {
19271         Roo.fly(this.getDragEl()).hide();
19272     },
19273
19274     // overrides Roo.dd.DragDrop
19275     // By default we try to move the element to the last location of the frame.
19276     // This is so that the default behavior mirrors that of Roo.dd.DD.
19277     endDrag: function(e) {
19278
19279         var lel = this.getEl();
19280         var del = this.getDragEl();
19281
19282         // Show the drag frame briefly so we can get its position
19283         del.style.visibility = "";
19284
19285         this.beforeMove();
19286         // Hide the linked element before the move to get around a Safari
19287         // rendering bug.
19288         lel.style.visibility = "hidden";
19289         Roo.dd.DDM.moveToEl(lel, del);
19290         del.style.visibility = "hidden";
19291         lel.style.visibility = "";
19292
19293         this.afterDrag();
19294     },
19295
19296     beforeMove : function(){
19297
19298     },
19299
19300     afterDrag : function(){
19301
19302     },
19303
19304     toString: function() {
19305         return ("DDProxy " + this.id);
19306     }
19307
19308 });
19309 /*
19310  * Based on:
19311  * Ext JS Library 1.1.1
19312  * Copyright(c) 2006-2007, Ext JS, LLC.
19313  *
19314  * Originally Released Under LGPL - original licence link has changed is not relivant.
19315  *
19316  * Fork - LGPL
19317  * <script type="text/javascript">
19318  */
19319
19320  /**
19321  * @class Roo.dd.DDTarget
19322  * A DragDrop implementation that does not move, but can be a drop
19323  * target.  You would get the same result by simply omitting implementation
19324  * for the event callbacks, but this way we reduce the processing cost of the
19325  * event listener and the callbacks.
19326  * @extends Roo.dd.DragDrop
19327  * @constructor
19328  * @param {String} id the id of the element that is a drop target
19329  * @param {String} sGroup the group of related DragDrop objects
19330  * @param {object} config an object containing configurable attributes
19331  *                 Valid properties for DDTarget in addition to those in
19332  *                 DragDrop:
19333  *                    none
19334  */
19335 Roo.dd.DDTarget = function(id, sGroup, config) {
19336     if (id) {
19337         this.initTarget(id, sGroup, config);
19338     }
19339     if (config.listeners || config.events) { 
19340        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19341             listeners : config.listeners || {}, 
19342             events : config.events || {} 
19343         });    
19344     }
19345 };
19346
19347 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19348 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19349     toString: function() {
19350         return ("DDTarget " + this.id);
19351     }
19352 });
19353 /*
19354  * Based on:
19355  * Ext JS Library 1.1.1
19356  * Copyright(c) 2006-2007, Ext JS, LLC.
19357  *
19358  * Originally Released Under LGPL - original licence link has changed is not relivant.
19359  *
19360  * Fork - LGPL
19361  * <script type="text/javascript">
19362  */
19363  
19364
19365 /**
19366  * @class Roo.dd.ScrollManager
19367  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19368  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19369  * @singleton
19370  */
19371 Roo.dd.ScrollManager = function(){
19372     var ddm = Roo.dd.DragDropMgr;
19373     var els = {};
19374     var dragEl = null;
19375     var proc = {};
19376     
19377     
19378     
19379     var onStop = function(e){
19380         dragEl = null;
19381         clearProc();
19382     };
19383     
19384     var triggerRefresh = function(){
19385         if(ddm.dragCurrent){
19386              ddm.refreshCache(ddm.dragCurrent.groups);
19387         }
19388     };
19389     
19390     var doScroll = function(){
19391         if(ddm.dragCurrent){
19392             var dds = Roo.dd.ScrollManager;
19393             if(!dds.animate){
19394                 if(proc.el.scroll(proc.dir, dds.increment)){
19395                     triggerRefresh();
19396                 }
19397             }else{
19398                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19399             }
19400         }
19401     };
19402     
19403     var clearProc = function(){
19404         if(proc.id){
19405             clearInterval(proc.id);
19406         }
19407         proc.id = 0;
19408         proc.el = null;
19409         proc.dir = "";
19410     };
19411     
19412     var startProc = function(el, dir){
19413          Roo.log('scroll startproc');
19414         clearProc();
19415         proc.el = el;
19416         proc.dir = dir;
19417         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19418     };
19419     
19420     var onFire = function(e, isDrop){
19421        
19422         if(isDrop || !ddm.dragCurrent){ return; }
19423         var dds = Roo.dd.ScrollManager;
19424         if(!dragEl || dragEl != ddm.dragCurrent){
19425             dragEl = ddm.dragCurrent;
19426             // refresh regions on drag start
19427             dds.refreshCache();
19428         }
19429         
19430         var xy = Roo.lib.Event.getXY(e);
19431         var pt = new Roo.lib.Point(xy[0], xy[1]);
19432         for(var id in els){
19433             var el = els[id], r = el._region;
19434             if(r && r.contains(pt) && el.isScrollable()){
19435                 if(r.bottom - pt.y <= dds.thresh){
19436                     if(proc.el != el){
19437                         startProc(el, "down");
19438                     }
19439                     return;
19440                 }else if(r.right - pt.x <= dds.thresh){
19441                     if(proc.el != el){
19442                         startProc(el, "left");
19443                     }
19444                     return;
19445                 }else if(pt.y - r.top <= dds.thresh){
19446                     if(proc.el != el){
19447                         startProc(el, "up");
19448                     }
19449                     return;
19450                 }else if(pt.x - r.left <= dds.thresh){
19451                     if(proc.el != el){
19452                         startProc(el, "right");
19453                     }
19454                     return;
19455                 }
19456             }
19457         }
19458         clearProc();
19459     };
19460     
19461     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19462     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19463     
19464     return {
19465         /**
19466          * Registers new overflow element(s) to auto scroll
19467          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19468          */
19469         register : function(el){
19470             if(el instanceof Array){
19471                 for(var i = 0, len = el.length; i < len; i++) {
19472                         this.register(el[i]);
19473                 }
19474             }else{
19475                 el = Roo.get(el);
19476                 els[el.id] = el;
19477             }
19478             Roo.dd.ScrollManager.els = els;
19479         },
19480         
19481         /**
19482          * Unregisters overflow element(s) so they are no longer scrolled
19483          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19484          */
19485         unregister : function(el){
19486             if(el instanceof Array){
19487                 for(var i = 0, len = el.length; i < len; i++) {
19488                         this.unregister(el[i]);
19489                 }
19490             }else{
19491                 el = Roo.get(el);
19492                 delete els[el.id];
19493             }
19494         },
19495         
19496         /**
19497          * The number of pixels from the edge of a container the pointer needs to be to 
19498          * trigger scrolling (defaults to 25)
19499          * @type Number
19500          */
19501         thresh : 25,
19502         
19503         /**
19504          * The number of pixels to scroll in each scroll increment (defaults to 50)
19505          * @type Number
19506          */
19507         increment : 100,
19508         
19509         /**
19510          * The frequency of scrolls in milliseconds (defaults to 500)
19511          * @type Number
19512          */
19513         frequency : 500,
19514         
19515         /**
19516          * True to animate the scroll (defaults to true)
19517          * @type Boolean
19518          */
19519         animate: true,
19520         
19521         /**
19522          * The animation duration in seconds - 
19523          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19524          * @type Number
19525          */
19526         animDuration: .4,
19527         
19528         /**
19529          * Manually trigger a cache refresh.
19530          */
19531         refreshCache : function(){
19532             for(var id in els){
19533                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19534                     els[id]._region = els[id].getRegion();
19535                 }
19536             }
19537         }
19538     };
19539 }();/*
19540  * Based on:
19541  * Ext JS Library 1.1.1
19542  * Copyright(c) 2006-2007, Ext JS, LLC.
19543  *
19544  * Originally Released Under LGPL - original licence link has changed is not relivant.
19545  *
19546  * Fork - LGPL
19547  * <script type="text/javascript">
19548  */
19549  
19550
19551 /**
19552  * @class Roo.dd.Registry
19553  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19554  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19555  * @singleton
19556  */
19557 Roo.dd.Registry = function(){
19558     var elements = {}; 
19559     var handles = {}; 
19560     var autoIdSeed = 0;
19561
19562     var getId = function(el, autogen){
19563         if(typeof el == "string"){
19564             return el;
19565         }
19566         var id = el.id;
19567         if(!id && autogen !== false){
19568             id = "roodd-" + (++autoIdSeed);
19569             el.id = id;
19570         }
19571         return id;
19572     };
19573     
19574     return {
19575     /**
19576      * Register a drag drop element
19577      * @param {String|HTMLElement} element The id or DOM node to register
19578      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19579      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19580      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19581      * populated in the data object (if applicable):
19582      * <pre>
19583 Value      Description<br />
19584 ---------  ------------------------------------------<br />
19585 handles    Array of DOM nodes that trigger dragging<br />
19586            for the element being registered<br />
19587 isHandle   True if the element passed in triggers<br />
19588            dragging itself, else false
19589 </pre>
19590      */
19591         register : function(el, data){
19592             data = data || {};
19593             if(typeof el == "string"){
19594                 el = document.getElementById(el);
19595             }
19596             data.ddel = el;
19597             elements[getId(el)] = data;
19598             if(data.isHandle !== false){
19599                 handles[data.ddel.id] = data;
19600             }
19601             if(data.handles){
19602                 var hs = data.handles;
19603                 for(var i = 0, len = hs.length; i < len; i++){
19604                         handles[getId(hs[i])] = data;
19605                 }
19606             }
19607         },
19608
19609     /**
19610      * Unregister a drag drop element
19611      * @param {String|HTMLElement}  element The id or DOM node to unregister
19612      */
19613         unregister : function(el){
19614             var id = getId(el, false);
19615             var data = elements[id];
19616             if(data){
19617                 delete elements[id];
19618                 if(data.handles){
19619                     var hs = data.handles;
19620                     for(var i = 0, len = hs.length; i < len; i++){
19621                         delete handles[getId(hs[i], false)];
19622                     }
19623                 }
19624             }
19625         },
19626
19627     /**
19628      * Returns the handle registered for a DOM Node by id
19629      * @param {String|HTMLElement} id The DOM node or id to look up
19630      * @return {Object} handle The custom handle data
19631      */
19632         getHandle : function(id){
19633             if(typeof id != "string"){ // must be element?
19634                 id = id.id;
19635             }
19636             return handles[id];
19637         },
19638
19639     /**
19640      * Returns the handle that is registered for the DOM node that is the target of the event
19641      * @param {Event} e The event
19642      * @return {Object} handle The custom handle data
19643      */
19644         getHandleFromEvent : function(e){
19645             var t = Roo.lib.Event.getTarget(e);
19646             return t ? handles[t.id] : null;
19647         },
19648
19649     /**
19650      * Returns a custom data object that is registered for a DOM node by id
19651      * @param {String|HTMLElement} id The DOM node or id to look up
19652      * @return {Object} data The custom data
19653      */
19654         getTarget : function(id){
19655             if(typeof id != "string"){ // must be element?
19656                 id = id.id;
19657             }
19658             return elements[id];
19659         },
19660
19661     /**
19662      * Returns a custom data object that is registered for the DOM node that is the target of the event
19663      * @param {Event} e The event
19664      * @return {Object} data The custom data
19665      */
19666         getTargetFromEvent : function(e){
19667             var t = Roo.lib.Event.getTarget(e);
19668             return t ? elements[t.id] || handles[t.id] : null;
19669         }
19670     };
19671 }();/*
19672  * Based on:
19673  * Ext JS Library 1.1.1
19674  * Copyright(c) 2006-2007, Ext JS, LLC.
19675  *
19676  * Originally Released Under LGPL - original licence link has changed is not relivant.
19677  *
19678  * Fork - LGPL
19679  * <script type="text/javascript">
19680  */
19681  
19682
19683 /**
19684  * @class Roo.dd.StatusProxy
19685  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19686  * default drag proxy used by all Roo.dd components.
19687  * @constructor
19688  * @param {Object} config
19689  */
19690 Roo.dd.StatusProxy = function(config){
19691     Roo.apply(this, config);
19692     this.id = this.id || Roo.id();
19693     this.el = new Roo.Layer({
19694         dh: {
19695             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19696                 {tag: "div", cls: "x-dd-drop-icon"},
19697                 {tag: "div", cls: "x-dd-drag-ghost"}
19698             ]
19699         }, 
19700         shadow: !config || config.shadow !== false
19701     });
19702     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19703     this.dropStatus = this.dropNotAllowed;
19704 };
19705
19706 Roo.dd.StatusProxy.prototype = {
19707     /**
19708      * @cfg {String} dropAllowed
19709      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19710      */
19711     dropAllowed : "x-dd-drop-ok",
19712     /**
19713      * @cfg {String} dropNotAllowed
19714      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19715      */
19716     dropNotAllowed : "x-dd-drop-nodrop",
19717
19718     /**
19719      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19720      * over the current target element.
19721      * @param {String} cssClass The css class for the new drop status indicator image
19722      */
19723     setStatus : function(cssClass){
19724         cssClass = cssClass || this.dropNotAllowed;
19725         if(this.dropStatus != cssClass){
19726             this.el.replaceClass(this.dropStatus, cssClass);
19727             this.dropStatus = cssClass;
19728         }
19729     },
19730
19731     /**
19732      * Resets the status indicator to the default dropNotAllowed value
19733      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19734      */
19735     reset : function(clearGhost){
19736         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19737         this.dropStatus = this.dropNotAllowed;
19738         if(clearGhost){
19739             this.ghost.update("");
19740         }
19741     },
19742
19743     /**
19744      * Updates the contents of the ghost element
19745      * @param {String} html The html that will replace the current innerHTML of the ghost element
19746      */
19747     update : function(html){
19748         if(typeof html == "string"){
19749             this.ghost.update(html);
19750         }else{
19751             this.ghost.update("");
19752             html.style.margin = "0";
19753             this.ghost.dom.appendChild(html);
19754         }
19755         // ensure float = none set?? cant remember why though.
19756         var el = this.ghost.dom.firstChild;
19757                 if(el){
19758                         Roo.fly(el).setStyle('float', 'none');
19759                 }
19760     },
19761     
19762     /**
19763      * Returns the underlying proxy {@link Roo.Layer}
19764      * @return {Roo.Layer} el
19765     */
19766     getEl : function(){
19767         return this.el;
19768     },
19769
19770     /**
19771      * Returns the ghost element
19772      * @return {Roo.Element} el
19773      */
19774     getGhost : function(){
19775         return this.ghost;
19776     },
19777
19778     /**
19779      * Hides the proxy
19780      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19781      */
19782     hide : function(clear){
19783         this.el.hide();
19784         if(clear){
19785             this.reset(true);
19786         }
19787     },
19788
19789     /**
19790      * Stops the repair animation if it's currently running
19791      */
19792     stop : function(){
19793         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19794             this.anim.stop();
19795         }
19796     },
19797
19798     /**
19799      * Displays this proxy
19800      */
19801     show : function(){
19802         this.el.show();
19803     },
19804
19805     /**
19806      * Force the Layer to sync its shadow and shim positions to the element
19807      */
19808     sync : function(){
19809         this.el.sync();
19810     },
19811
19812     /**
19813      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19814      * invalid drop operation by the item being dragged.
19815      * @param {Array} xy The XY position of the element ([x, y])
19816      * @param {Function} callback The function to call after the repair is complete
19817      * @param {Object} scope The scope in which to execute the callback
19818      */
19819     repair : function(xy, callback, scope){
19820         this.callback = callback;
19821         this.scope = scope;
19822         if(xy && this.animRepair !== false){
19823             this.el.addClass("x-dd-drag-repair");
19824             this.el.hideUnders(true);
19825             this.anim = this.el.shift({
19826                 duration: this.repairDuration || .5,
19827                 easing: 'easeOut',
19828                 xy: xy,
19829                 stopFx: true,
19830                 callback: this.afterRepair,
19831                 scope: this
19832             });
19833         }else{
19834             this.afterRepair();
19835         }
19836     },
19837
19838     // private
19839     afterRepair : function(){
19840         this.hide(true);
19841         if(typeof this.callback == "function"){
19842             this.callback.call(this.scope || this);
19843         }
19844         this.callback = null;
19845         this.scope = null;
19846     }
19847 };/*
19848  * Based on:
19849  * Ext JS Library 1.1.1
19850  * Copyright(c) 2006-2007, Ext JS, LLC.
19851  *
19852  * Originally Released Under LGPL - original licence link has changed is not relivant.
19853  *
19854  * Fork - LGPL
19855  * <script type="text/javascript">
19856  */
19857
19858 /**
19859  * @class Roo.dd.DragSource
19860  * @extends Roo.dd.DDProxy
19861  * A simple class that provides the basic implementation needed to make any element draggable.
19862  * @constructor
19863  * @param {String/HTMLElement/Element} el The container element
19864  * @param {Object} config
19865  */
19866 Roo.dd.DragSource = function(el, config){
19867     this.el = Roo.get(el);
19868     this.dragData = {};
19869     
19870     Roo.apply(this, config);
19871     
19872     if(!this.proxy){
19873         this.proxy = new Roo.dd.StatusProxy();
19874     }
19875
19876     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19877           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19878     
19879     this.dragging = false;
19880 };
19881
19882 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19883     /**
19884      * @cfg {String} dropAllowed
19885      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19886      */
19887     dropAllowed : "x-dd-drop-ok",
19888     /**
19889      * @cfg {String} dropNotAllowed
19890      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19891      */
19892     dropNotAllowed : "x-dd-drop-nodrop",
19893
19894     /**
19895      * Returns the data object associated with this drag source
19896      * @return {Object} data An object containing arbitrary data
19897      */
19898     getDragData : function(e){
19899         return this.dragData;
19900     },
19901
19902     // private
19903     onDragEnter : function(e, id){
19904         var target = Roo.dd.DragDropMgr.getDDById(id);
19905         this.cachedTarget = target;
19906         if(this.beforeDragEnter(target, e, id) !== false){
19907             if(target.isNotifyTarget){
19908                 var status = target.notifyEnter(this, e, this.dragData);
19909                 this.proxy.setStatus(status);
19910             }else{
19911                 this.proxy.setStatus(this.dropAllowed);
19912             }
19913             
19914             if(this.afterDragEnter){
19915                 /**
19916                  * An empty function by default, but provided so that you can perform a custom action
19917                  * when the dragged item enters the drop target by providing an implementation.
19918                  * @param {Roo.dd.DragDrop} target The drop target
19919                  * @param {Event} e The event object
19920                  * @param {String} id The id of the dragged element
19921                  * @method afterDragEnter
19922                  */
19923                 this.afterDragEnter(target, e, id);
19924             }
19925         }
19926     },
19927
19928     /**
19929      * An empty function by default, but provided so that you can perform a custom action
19930      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19931      * @param {Roo.dd.DragDrop} target The drop target
19932      * @param {Event} e The event object
19933      * @param {String} id The id of the dragged element
19934      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19935      */
19936     beforeDragEnter : function(target, e, id){
19937         return true;
19938     },
19939
19940     // private
19941     alignElWithMouse: function() {
19942         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19943         this.proxy.sync();
19944     },
19945
19946     // private
19947     onDragOver : function(e, id){
19948         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19949         if(this.beforeDragOver(target, e, id) !== false){
19950             if(target.isNotifyTarget){
19951                 var status = target.notifyOver(this, e, this.dragData);
19952                 this.proxy.setStatus(status);
19953             }
19954
19955             if(this.afterDragOver){
19956                 /**
19957                  * An empty function by default, but provided so that you can perform a custom action
19958                  * while the dragged item is over the drop target by providing an implementation.
19959                  * @param {Roo.dd.DragDrop} target The drop target
19960                  * @param {Event} e The event object
19961                  * @param {String} id The id of the dragged element
19962                  * @method afterDragOver
19963                  */
19964                 this.afterDragOver(target, e, id);
19965             }
19966         }
19967     },
19968
19969     /**
19970      * An empty function by default, but provided so that you can perform a custom action
19971      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19972      * @param {Roo.dd.DragDrop} target The drop target
19973      * @param {Event} e The event object
19974      * @param {String} id The id of the dragged element
19975      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19976      */
19977     beforeDragOver : function(target, e, id){
19978         return true;
19979     },
19980
19981     // private
19982     onDragOut : function(e, id){
19983         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19984         if(this.beforeDragOut(target, e, id) !== false){
19985             if(target.isNotifyTarget){
19986                 target.notifyOut(this, e, this.dragData);
19987             }
19988             this.proxy.reset();
19989             if(this.afterDragOut){
19990                 /**
19991                  * An empty function by default, but provided so that you can perform a custom action
19992                  * after the dragged item is dragged out of the target without dropping.
19993                  * @param {Roo.dd.DragDrop} target The drop target
19994                  * @param {Event} e The event object
19995                  * @param {String} id The id of the dragged element
19996                  * @method afterDragOut
19997                  */
19998                 this.afterDragOut(target, e, id);
19999             }
20000         }
20001         this.cachedTarget = null;
20002     },
20003
20004     /**
20005      * An empty function by default, but provided so that you can perform a custom action before the dragged
20006      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20007      * @param {Roo.dd.DragDrop} target The drop target
20008      * @param {Event} e The event object
20009      * @param {String} id The id of the dragged element
20010      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20011      */
20012     beforeDragOut : function(target, e, id){
20013         return true;
20014     },
20015     
20016     // private
20017     onDragDrop : function(e, id){
20018         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20019         if(this.beforeDragDrop(target, e, id) !== false){
20020             if(target.isNotifyTarget){
20021                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20022                     this.onValidDrop(target, e, id);
20023                 }else{
20024                     this.onInvalidDrop(target, e, id);
20025                 }
20026             }else{
20027                 this.onValidDrop(target, e, id);
20028             }
20029             
20030             if(this.afterDragDrop){
20031                 /**
20032                  * An empty function by default, but provided so that you can perform a custom action
20033                  * after a valid drag drop has occurred by providing an implementation.
20034                  * @param {Roo.dd.DragDrop} target The drop target
20035                  * @param {Event} e The event object
20036                  * @param {String} id The id of the dropped element
20037                  * @method afterDragDrop
20038                  */
20039                 this.afterDragDrop(target, e, id);
20040             }
20041         }
20042         delete this.cachedTarget;
20043     },
20044
20045     /**
20046      * An empty function by default, but provided so that you can perform a custom action before the dragged
20047      * item is dropped onto the target and optionally cancel the onDragDrop.
20048      * @param {Roo.dd.DragDrop} target The drop target
20049      * @param {Event} e The event object
20050      * @param {String} id The id of the dragged element
20051      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20052      */
20053     beforeDragDrop : function(target, e, id){
20054         return true;
20055     },
20056
20057     // private
20058     onValidDrop : function(target, e, id){
20059         this.hideProxy();
20060         if(this.afterValidDrop){
20061             /**
20062              * An empty function by default, but provided so that you can perform a custom action
20063              * after a valid drop has occurred by providing an implementation.
20064              * @param {Object} target The target DD 
20065              * @param {Event} e The event object
20066              * @param {String} id The id of the dropped element
20067              * @method afterInvalidDrop
20068              */
20069             this.afterValidDrop(target, e, id);
20070         }
20071     },
20072
20073     // private
20074     getRepairXY : function(e, data){
20075         return this.el.getXY();  
20076     },
20077
20078     // private
20079     onInvalidDrop : function(target, e, id){
20080         this.beforeInvalidDrop(target, e, id);
20081         if(this.cachedTarget){
20082             if(this.cachedTarget.isNotifyTarget){
20083                 this.cachedTarget.notifyOut(this, e, this.dragData);
20084             }
20085             this.cacheTarget = null;
20086         }
20087         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20088
20089         if(this.afterInvalidDrop){
20090             /**
20091              * An empty function by default, but provided so that you can perform a custom action
20092              * after an invalid drop has occurred by providing an implementation.
20093              * @param {Event} e The event object
20094              * @param {String} id The id of the dropped element
20095              * @method afterInvalidDrop
20096              */
20097             this.afterInvalidDrop(e, id);
20098         }
20099     },
20100
20101     // private
20102     afterRepair : function(){
20103         if(Roo.enableFx){
20104             this.el.highlight(this.hlColor || "c3daf9");
20105         }
20106         this.dragging = false;
20107     },
20108
20109     /**
20110      * An empty function by default, but provided so that you can perform a custom action after an invalid
20111      * drop has occurred.
20112      * @param {Roo.dd.DragDrop} target The drop target
20113      * @param {Event} e The event object
20114      * @param {String} id The id of the dragged element
20115      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20116      */
20117     beforeInvalidDrop : function(target, e, id){
20118         return true;
20119     },
20120
20121     // private
20122     handleMouseDown : function(e){
20123         if(this.dragging) {
20124             return;
20125         }
20126         var data = this.getDragData(e);
20127         if(data && this.onBeforeDrag(data, e) !== false){
20128             this.dragData = data;
20129             this.proxy.stop();
20130             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20131         } 
20132     },
20133
20134     /**
20135      * An empty function by default, but provided so that you can perform a custom action before the initial
20136      * drag event begins and optionally cancel it.
20137      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20138      * @param {Event} e The event object
20139      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20140      */
20141     onBeforeDrag : function(data, e){
20142         return true;
20143     },
20144
20145     /**
20146      * An empty function by default, but provided so that you can perform a custom action once the initial
20147      * drag event has begun.  The drag cannot be canceled from this function.
20148      * @param {Number} x The x position of the click on the dragged object
20149      * @param {Number} y The y position of the click on the dragged object
20150      */
20151     onStartDrag : Roo.emptyFn,
20152
20153     // private - YUI override
20154     startDrag : function(x, y){
20155         this.proxy.reset();
20156         this.dragging = true;
20157         this.proxy.update("");
20158         this.onInitDrag(x, y);
20159         this.proxy.show();
20160     },
20161
20162     // private
20163     onInitDrag : function(x, y){
20164         var clone = this.el.dom.cloneNode(true);
20165         clone.id = Roo.id(); // prevent duplicate ids
20166         this.proxy.update(clone);
20167         this.onStartDrag(x, y);
20168         return true;
20169     },
20170
20171     /**
20172      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20173      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20174      */
20175     getProxy : function(){
20176         return this.proxy;  
20177     },
20178
20179     /**
20180      * Hides the drag source's {@link Roo.dd.StatusProxy}
20181      */
20182     hideProxy : function(){
20183         this.proxy.hide();  
20184         this.proxy.reset(true);
20185         this.dragging = false;
20186     },
20187
20188     // private
20189     triggerCacheRefresh : function(){
20190         Roo.dd.DDM.refreshCache(this.groups);
20191     },
20192
20193     // private - override to prevent hiding
20194     b4EndDrag: function(e) {
20195     },
20196
20197     // private - override to prevent moving
20198     endDrag : function(e){
20199         this.onEndDrag(this.dragData, e);
20200     },
20201
20202     // private
20203     onEndDrag : function(data, e){
20204     },
20205     
20206     // private - pin to cursor
20207     autoOffset : function(x, y) {
20208         this.setDelta(-12, -20);
20209     }    
20210 });/*
20211  * Based on:
20212  * Ext JS Library 1.1.1
20213  * Copyright(c) 2006-2007, Ext JS, LLC.
20214  *
20215  * Originally Released Under LGPL - original licence link has changed is not relivant.
20216  *
20217  * Fork - LGPL
20218  * <script type="text/javascript">
20219  */
20220
20221
20222 /**
20223  * @class Roo.dd.DropTarget
20224  * @extends Roo.dd.DDTarget
20225  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20226  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20227  * @constructor
20228  * @param {String/HTMLElement/Element} el The container element
20229  * @param {Object} config
20230  */
20231 Roo.dd.DropTarget = function(el, config){
20232     this.el = Roo.get(el);
20233     
20234     var listeners = false; ;
20235     if (config && config.listeners) {
20236         listeners= config.listeners;
20237         delete config.listeners;
20238     }
20239     Roo.apply(this, config);
20240     
20241     if(this.containerScroll){
20242         Roo.dd.ScrollManager.register(this.el);
20243     }
20244     this.addEvents( {
20245          /**
20246          * @scope Roo.dd.DropTarget
20247          */
20248          
20249          /**
20250          * @event enter
20251          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20252          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20253          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20254          * 
20255          * IMPORTANT : it should set this.overClass and this.dropAllowed
20256          * 
20257          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20258          * @param {Event} e The event
20259          * @param {Object} data An object containing arbitrary data supplied by the drag source
20260          */
20261         "enter" : true,
20262         
20263          /**
20264          * @event over
20265          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20266          * This method will be called on every mouse movement while the drag source is over the drop target.
20267          * This default implementation simply returns the dropAllowed config value.
20268          * 
20269          * IMPORTANT : it should set this.dropAllowed
20270          * 
20271          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20272          * @param {Event} e The event
20273          * @param {Object} data An object containing arbitrary data supplied by the drag source
20274          
20275          */
20276         "over" : true,
20277         /**
20278          * @event out
20279          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20280          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20281          * overClass (if any) from the drop element.
20282          * 
20283          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20284          * @param {Event} e The event
20285          * @param {Object} data An object containing arbitrary data supplied by the drag source
20286          */
20287          "out" : true,
20288          
20289         /**
20290          * @event drop
20291          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20292          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20293          * implementation that does something to process the drop event and returns true so that the drag source's
20294          * repair action does not run.
20295          * 
20296          * IMPORTANT : it should set this.success
20297          * 
20298          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20299          * @param {Event} e The event
20300          * @param {Object} data An object containing arbitrary data supplied by the drag source
20301         */
20302          "drop" : true
20303     });
20304             
20305      
20306     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20307         this.el.dom, 
20308         this.ddGroup || this.group,
20309         {
20310             isTarget: true,
20311             listeners : listeners || {} 
20312            
20313         
20314         }
20315     );
20316
20317 };
20318
20319 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20320     /**
20321      * @cfg {String} overClass
20322      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20323      */
20324      /**
20325      * @cfg {String} ddGroup
20326      * The drag drop group to handle drop events for
20327      */
20328      
20329     /**
20330      * @cfg {String} dropAllowed
20331      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20332      */
20333     dropAllowed : "x-dd-drop-ok",
20334     /**
20335      * @cfg {String} dropNotAllowed
20336      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20337      */
20338     dropNotAllowed : "x-dd-drop-nodrop",
20339     /**
20340      * @cfg {boolean} success
20341      * set this after drop listener.. 
20342      */
20343     success : false,
20344     /**
20345      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20346      * if the drop point is valid for over/enter..
20347      */
20348     valid : false,
20349     // private
20350     isTarget : true,
20351
20352     // private
20353     isNotifyTarget : true,
20354     
20355     /**
20356      * @hide
20357      */
20358     notifyEnter : function(dd, e, data)
20359     {
20360         this.valid = true;
20361         this.fireEvent('enter', dd, e, data);
20362         if(this.overClass){
20363             this.el.addClass(this.overClass);
20364         }
20365         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20366             this.valid ? this.dropAllowed : this.dropNotAllowed
20367         );
20368     },
20369
20370     /**
20371      * @hide
20372      */
20373     notifyOver : function(dd, e, data)
20374     {
20375         this.valid = true;
20376         this.fireEvent('over', dd, e, data);
20377         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20378             this.valid ? this.dropAllowed : this.dropNotAllowed
20379         );
20380     },
20381
20382     /**
20383      * @hide
20384      */
20385     notifyOut : function(dd, e, data)
20386     {
20387         this.fireEvent('out', dd, e, data);
20388         if(this.overClass){
20389             this.el.removeClass(this.overClass);
20390         }
20391     },
20392
20393     /**
20394      * @hide
20395      */
20396     notifyDrop : function(dd, e, data)
20397     {
20398         this.success = false;
20399         this.fireEvent('drop', dd, e, data);
20400         return this.success;
20401     }
20402 });/*
20403  * Based on:
20404  * Ext JS Library 1.1.1
20405  * Copyright(c) 2006-2007, Ext JS, LLC.
20406  *
20407  * Originally Released Under LGPL - original licence link has changed is not relivant.
20408  *
20409  * Fork - LGPL
20410  * <script type="text/javascript">
20411  */
20412
20413
20414 /**
20415  * @class Roo.dd.DragZone
20416  * @extends Roo.dd.DragSource
20417  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20418  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20419  * @constructor
20420  * @param {String/HTMLElement/Element} el The container element
20421  * @param {Object} config
20422  */
20423 Roo.dd.DragZone = function(el, config){
20424     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20425     if(this.containerScroll){
20426         Roo.dd.ScrollManager.register(this.el);
20427     }
20428 };
20429
20430 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20431     /**
20432      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20433      * for auto scrolling during drag operations.
20434      */
20435     /**
20436      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20437      * method after a failed drop (defaults to "c3daf9" - light blue)
20438      */
20439
20440     /**
20441      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20442      * for a valid target to drag based on the mouse down. Override this method
20443      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20444      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20445      * @param {EventObject} e The mouse down event
20446      * @return {Object} The dragData
20447      */
20448     getDragData : function(e){
20449         return Roo.dd.Registry.getHandleFromEvent(e);
20450     },
20451     
20452     /**
20453      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20454      * this.dragData.ddel
20455      * @param {Number} x The x position of the click on the dragged object
20456      * @param {Number} y The y position of the click on the dragged object
20457      * @return {Boolean} true to continue the drag, false to cancel
20458      */
20459     onInitDrag : function(x, y){
20460         this.proxy.update(this.dragData.ddel.cloneNode(true));
20461         this.onStartDrag(x, y);
20462         return true;
20463     },
20464     
20465     /**
20466      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20467      */
20468     afterRepair : function(){
20469         if(Roo.enableFx){
20470             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20471         }
20472         this.dragging = false;
20473     },
20474
20475     /**
20476      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20477      * the XY of this.dragData.ddel
20478      * @param {EventObject} e The mouse up event
20479      * @return {Array} The xy location (e.g. [100, 200])
20480      */
20481     getRepairXY : function(e){
20482         return Roo.Element.fly(this.dragData.ddel).getXY();  
20483     }
20484 });/*
20485  * Based on:
20486  * Ext JS Library 1.1.1
20487  * Copyright(c) 2006-2007, Ext JS, LLC.
20488  *
20489  * Originally Released Under LGPL - original licence link has changed is not relivant.
20490  *
20491  * Fork - LGPL
20492  * <script type="text/javascript">
20493  */
20494 /**
20495  * @class Roo.dd.DropZone
20496  * @extends Roo.dd.DropTarget
20497  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20498  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20499  * @constructor
20500  * @param {String/HTMLElement/Element} el The container element
20501  * @param {Object} config
20502  */
20503 Roo.dd.DropZone = function(el, config){
20504     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20505 };
20506
20507 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20508     /**
20509      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20510      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20511      * provide your own custom lookup.
20512      * @param {Event} e The event
20513      * @return {Object} data The custom data
20514      */
20515     getTargetFromEvent : function(e){
20516         return Roo.dd.Registry.getTargetFromEvent(e);
20517     },
20518
20519     /**
20520      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20521      * that it has registered.  This method has no default implementation and should be overridden to provide
20522      * node-specific processing if necessary.
20523      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20524      * {@link #getTargetFromEvent} for this node)
20525      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20526      * @param {Event} e The event
20527      * @param {Object} data An object containing arbitrary data supplied by the drag source
20528      */
20529     onNodeEnter : function(n, dd, e, data){
20530         
20531     },
20532
20533     /**
20534      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20535      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20536      * overridden to provide the proper feedback.
20537      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20538      * {@link #getTargetFromEvent} for this node)
20539      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20540      * @param {Event} e The event
20541      * @param {Object} data An object containing arbitrary data supplied by the drag source
20542      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20543      * underlying {@link Roo.dd.StatusProxy} can be updated
20544      */
20545     onNodeOver : function(n, dd, e, data){
20546         return this.dropAllowed;
20547     },
20548
20549     /**
20550      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20551      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20552      * node-specific processing if necessary.
20553      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20554      * {@link #getTargetFromEvent} for this node)
20555      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20556      * @param {Event} e The event
20557      * @param {Object} data An object containing arbitrary data supplied by the drag source
20558      */
20559     onNodeOut : function(n, dd, e, data){
20560         
20561     },
20562
20563     /**
20564      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20565      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20566      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20567      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20568      * {@link #getTargetFromEvent} for this node)
20569      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20570      * @param {Event} e The event
20571      * @param {Object} data An object containing arbitrary data supplied by the drag source
20572      * @return {Boolean} True if the drop was valid, else false
20573      */
20574     onNodeDrop : function(n, dd, e, data){
20575         return false;
20576     },
20577
20578     /**
20579      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20580      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20581      * it should be overridden to provide the proper feedback if necessary.
20582      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20583      * @param {Event} e The event
20584      * @param {Object} data An object containing arbitrary data supplied by the drag source
20585      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20586      * underlying {@link Roo.dd.StatusProxy} can be updated
20587      */
20588     onContainerOver : function(dd, e, data){
20589         return this.dropNotAllowed;
20590     },
20591
20592     /**
20593      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20594      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20595      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20596      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20597      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20598      * @param {Event} e The event
20599      * @param {Object} data An object containing arbitrary data supplied by the drag source
20600      * @return {Boolean} True if the drop was valid, else false
20601      */
20602     onContainerDrop : function(dd, e, data){
20603         return false;
20604     },
20605
20606     /**
20607      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20608      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20609      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20610      * you should override this method and provide a custom implementation.
20611      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20612      * @param {Event} e The event
20613      * @param {Object} data An object containing arbitrary data supplied by the drag source
20614      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20615      * underlying {@link Roo.dd.StatusProxy} can be updated
20616      */
20617     notifyEnter : function(dd, e, data){
20618         return this.dropNotAllowed;
20619     },
20620
20621     /**
20622      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20623      * This method will be called on every mouse movement while the drag source is over the drop zone.
20624      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20625      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20626      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20627      * registered node, it will call {@link #onContainerOver}.
20628      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20629      * @param {Event} e The event
20630      * @param {Object} data An object containing arbitrary data supplied by the drag source
20631      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20632      * underlying {@link Roo.dd.StatusProxy} can be updated
20633      */
20634     notifyOver : function(dd, e, data){
20635         var n = this.getTargetFromEvent(e);
20636         if(!n){ // not over valid drop target
20637             if(this.lastOverNode){
20638                 this.onNodeOut(this.lastOverNode, dd, e, data);
20639                 this.lastOverNode = null;
20640             }
20641             return this.onContainerOver(dd, e, data);
20642         }
20643         if(this.lastOverNode != n){
20644             if(this.lastOverNode){
20645                 this.onNodeOut(this.lastOverNode, dd, e, data);
20646             }
20647             this.onNodeEnter(n, dd, e, data);
20648             this.lastOverNode = n;
20649         }
20650         return this.onNodeOver(n, dd, e, data);
20651     },
20652
20653     /**
20654      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20655      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20656      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20657      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20658      * @param {Event} e The event
20659      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20660      */
20661     notifyOut : function(dd, e, data){
20662         if(this.lastOverNode){
20663             this.onNodeOut(this.lastOverNode, dd, e, data);
20664             this.lastOverNode = null;
20665         }
20666     },
20667
20668     /**
20669      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20670      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20671      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20672      * otherwise it will call {@link #onContainerDrop}.
20673      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20674      * @param {Event} e The event
20675      * @param {Object} data An object containing arbitrary data supplied by the drag source
20676      * @return {Boolean} True if the drop was valid, else false
20677      */
20678     notifyDrop : function(dd, e, data){
20679         if(this.lastOverNode){
20680             this.onNodeOut(this.lastOverNode, dd, e, data);
20681             this.lastOverNode = null;
20682         }
20683         var n = this.getTargetFromEvent(e);
20684         return n ?
20685             this.onNodeDrop(n, dd, e, data) :
20686             this.onContainerDrop(dd, e, data);
20687     },
20688
20689     // private
20690     triggerCacheRefresh : function(){
20691         Roo.dd.DDM.refreshCache(this.groups);
20692     }  
20693 });/*
20694  * Based on:
20695  * Ext JS Library 1.1.1
20696  * Copyright(c) 2006-2007, Ext JS, LLC.
20697  *
20698  * Originally Released Under LGPL - original licence link has changed is not relivant.
20699  *
20700  * Fork - LGPL
20701  * <script type="text/javascript">
20702  */
20703
20704
20705 /**
20706  * @class Roo.data.SortTypes
20707  * @singleton
20708  * Defines the default sorting (casting?) comparison functions used when sorting data.
20709  */
20710 Roo.data.SortTypes = {
20711     /**
20712      * Default sort that does nothing
20713      * @param {Mixed} s The value being converted
20714      * @return {Mixed} The comparison value
20715      */
20716     none : function(s){
20717         return s;
20718     },
20719     
20720     /**
20721      * The regular expression used to strip tags
20722      * @type {RegExp}
20723      * @property
20724      */
20725     stripTagsRE : /<\/?[^>]+>/gi,
20726     
20727     /**
20728      * Strips all HTML tags to sort on text only
20729      * @param {Mixed} s The value being converted
20730      * @return {String} The comparison value
20731      */
20732     asText : function(s){
20733         return String(s).replace(this.stripTagsRE, "");
20734     },
20735     
20736     /**
20737      * Strips all HTML tags to sort on text only - Case insensitive
20738      * @param {Mixed} s The value being converted
20739      * @return {String} The comparison value
20740      */
20741     asUCText : function(s){
20742         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20743     },
20744     
20745     /**
20746      * Case insensitive string
20747      * @param {Mixed} s The value being converted
20748      * @return {String} The comparison value
20749      */
20750     asUCString : function(s) {
20751         return String(s).toUpperCase();
20752     },
20753     
20754     /**
20755      * Date sorting
20756      * @param {Mixed} s The value being converted
20757      * @return {Number} The comparison value
20758      */
20759     asDate : function(s) {
20760         if(!s){
20761             return 0;
20762         }
20763         if(s instanceof Date){
20764             return s.getTime();
20765         }
20766         return Date.parse(String(s));
20767     },
20768     
20769     /**
20770      * Float sorting
20771      * @param {Mixed} s The value being converted
20772      * @return {Float} The comparison value
20773      */
20774     asFloat : function(s) {
20775         var val = parseFloat(String(s).replace(/,/g, ""));
20776         if(isNaN(val)) val = 0;
20777         return val;
20778     },
20779     
20780     /**
20781      * Integer sorting
20782      * @param {Mixed} s The value being converted
20783      * @return {Number} The comparison value
20784      */
20785     asInt : function(s) {
20786         var val = parseInt(String(s).replace(/,/g, ""));
20787         if(isNaN(val)) val = 0;
20788         return val;
20789     }
20790 };/*
20791  * Based on:
20792  * Ext JS Library 1.1.1
20793  * Copyright(c) 2006-2007, Ext JS, LLC.
20794  *
20795  * Originally Released Under LGPL - original licence link has changed is not relivant.
20796  *
20797  * Fork - LGPL
20798  * <script type="text/javascript">
20799  */
20800
20801 /**
20802 * @class Roo.data.Record
20803  * Instances of this class encapsulate both record <em>definition</em> information, and record
20804  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20805  * to access Records cached in an {@link Roo.data.Store} object.<br>
20806  * <p>
20807  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20808  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20809  * objects.<br>
20810  * <p>
20811  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20812  * @constructor
20813  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20814  * {@link #create}. The parameters are the same.
20815  * @param {Array} data An associative Array of data values keyed by the field name.
20816  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20817  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20818  * not specified an integer id is generated.
20819  */
20820 Roo.data.Record = function(data, id){
20821     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20822     this.data = data;
20823 };
20824
20825 /**
20826  * Generate a constructor for a specific record layout.
20827  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20828  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20829  * Each field definition object may contain the following properties: <ul>
20830  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
20831  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20832  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20833  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20834  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20835  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20836  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20837  * this may be omitted.</p></li>
20838  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20839  * <ul><li>auto (Default, implies no conversion)</li>
20840  * <li>string</li>
20841  * <li>int</li>
20842  * <li>float</li>
20843  * <li>boolean</li>
20844  * <li>date</li></ul></p></li>
20845  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20846  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20847  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20848  * by the Reader into an object that will be stored in the Record. It is passed the
20849  * following parameters:<ul>
20850  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20851  * </ul></p></li>
20852  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20853  * </ul>
20854  * <br>usage:<br><pre><code>
20855 var TopicRecord = Roo.data.Record.create(
20856     {name: 'title', mapping: 'topic_title'},
20857     {name: 'author', mapping: 'username'},
20858     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20859     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20860     {name: 'lastPoster', mapping: 'user2'},
20861     {name: 'excerpt', mapping: 'post_text'}
20862 );
20863
20864 var myNewRecord = new TopicRecord({
20865     title: 'Do my job please',
20866     author: 'noobie',
20867     totalPosts: 1,
20868     lastPost: new Date(),
20869     lastPoster: 'Animal',
20870     excerpt: 'No way dude!'
20871 });
20872 myStore.add(myNewRecord);
20873 </code></pre>
20874  * @method create
20875  * @static
20876  */
20877 Roo.data.Record.create = function(o){
20878     var f = function(){
20879         f.superclass.constructor.apply(this, arguments);
20880     };
20881     Roo.extend(f, Roo.data.Record);
20882     var p = f.prototype;
20883     p.fields = new Roo.util.MixedCollection(false, function(field){
20884         return field.name;
20885     });
20886     for(var i = 0, len = o.length; i < len; i++){
20887         p.fields.add(new Roo.data.Field(o[i]));
20888     }
20889     f.getField = function(name){
20890         return p.fields.get(name);  
20891     };
20892     return f;
20893 };
20894
20895 Roo.data.Record.AUTO_ID = 1000;
20896 Roo.data.Record.EDIT = 'edit';
20897 Roo.data.Record.REJECT = 'reject';
20898 Roo.data.Record.COMMIT = 'commit';
20899
20900 Roo.data.Record.prototype = {
20901     /**
20902      * Readonly flag - true if this record has been modified.
20903      * @type Boolean
20904      */
20905     dirty : false,
20906     editing : false,
20907     error: null,
20908     modified: null,
20909
20910     // private
20911     join : function(store){
20912         this.store = store;
20913     },
20914
20915     /**
20916      * Set the named field to the specified value.
20917      * @param {String} name The name of the field to set.
20918      * @param {Object} value The value to set the field to.
20919      */
20920     set : function(name, value){
20921         if(this.data[name] == value){
20922             return;
20923         }
20924         this.dirty = true;
20925         if(!this.modified){
20926             this.modified = {};
20927         }
20928         if(typeof this.modified[name] == 'undefined'){
20929             this.modified[name] = this.data[name];
20930         }
20931         this.data[name] = value;
20932         if(!this.editing && this.store){
20933             this.store.afterEdit(this);
20934         }       
20935     },
20936
20937     /**
20938      * Get the value of the named field.
20939      * @param {String} name The name of the field to get the value of.
20940      * @return {Object} The value of the field.
20941      */
20942     get : function(name){
20943         return this.data[name]; 
20944     },
20945
20946     // private
20947     beginEdit : function(){
20948         this.editing = true;
20949         this.modified = {}; 
20950     },
20951
20952     // private
20953     cancelEdit : function(){
20954         this.editing = false;
20955         delete this.modified;
20956     },
20957
20958     // private
20959     endEdit : function(){
20960         this.editing = false;
20961         if(this.dirty && this.store){
20962             this.store.afterEdit(this);
20963         }
20964     },
20965
20966     /**
20967      * Usually called by the {@link Roo.data.Store} which owns the Record.
20968      * Rejects all changes made to the Record since either creation, or the last commit operation.
20969      * Modified fields are reverted to their original values.
20970      * <p>
20971      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20972      * of reject operations.
20973      */
20974     reject : function(){
20975         var m = this.modified;
20976         for(var n in m){
20977             if(typeof m[n] != "function"){
20978                 this.data[n] = m[n];
20979             }
20980         }
20981         this.dirty = false;
20982         delete this.modified;
20983         this.editing = false;
20984         if(this.store){
20985             this.store.afterReject(this);
20986         }
20987     },
20988
20989     /**
20990      * Usually called by the {@link Roo.data.Store} which owns the Record.
20991      * Commits all changes made to the Record since either creation, or the last commit operation.
20992      * <p>
20993      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20994      * of commit operations.
20995      */
20996     commit : function(){
20997         this.dirty = false;
20998         delete this.modified;
20999         this.editing = false;
21000         if(this.store){
21001             this.store.afterCommit(this);
21002         }
21003     },
21004
21005     // private
21006     hasError : function(){
21007         return this.error != null;
21008     },
21009
21010     // private
21011     clearError : function(){
21012         this.error = null;
21013     },
21014
21015     /**
21016      * Creates a copy of this record.
21017      * @param {String} id (optional) A new record id if you don't want to use this record's id
21018      * @return {Record}
21019      */
21020     copy : function(newId) {
21021         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21022     }
21023 };/*
21024  * Based on:
21025  * Ext JS Library 1.1.1
21026  * Copyright(c) 2006-2007, Ext JS, LLC.
21027  *
21028  * Originally Released Under LGPL - original licence link has changed is not relivant.
21029  *
21030  * Fork - LGPL
21031  * <script type="text/javascript">
21032  */
21033
21034
21035
21036 /**
21037  * @class Roo.data.Store
21038  * @extends Roo.util.Observable
21039  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21040  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21041  * <p>
21042  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
21043  * has no knowledge of the format of the data returned by the Proxy.<br>
21044  * <p>
21045  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21046  * instances from the data object. These records are cached and made available through accessor functions.
21047  * @constructor
21048  * Creates a new Store.
21049  * @param {Object} config A config object containing the objects needed for the Store to access data,
21050  * and read the data into Records.
21051  */
21052 Roo.data.Store = function(config){
21053     this.data = new Roo.util.MixedCollection(false);
21054     this.data.getKey = function(o){
21055         return o.id;
21056     };
21057     this.baseParams = {};
21058     // private
21059     this.paramNames = {
21060         "start" : "start",
21061         "limit" : "limit",
21062         "sort" : "sort",
21063         "dir" : "dir",
21064         "multisort" : "_multisort"
21065     };
21066
21067     if(config && config.data){
21068         this.inlineData = config.data;
21069         delete config.data;
21070     }
21071
21072     Roo.apply(this, config);
21073     
21074     if(this.reader){ // reader passed
21075         this.reader = Roo.factory(this.reader, Roo.data);
21076         this.reader.xmodule = this.xmodule || false;
21077         if(!this.recordType){
21078             this.recordType = this.reader.recordType;
21079         }
21080         if(this.reader.onMetaChange){
21081             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21082         }
21083     }
21084
21085     if(this.recordType){
21086         this.fields = this.recordType.prototype.fields;
21087     }
21088     this.modified = [];
21089
21090     this.addEvents({
21091         /**
21092          * @event datachanged
21093          * Fires when the data cache has changed, and a widget which is using this Store
21094          * as a Record cache should refresh its view.
21095          * @param {Store} this
21096          */
21097         datachanged : true,
21098         /**
21099          * @event metachange
21100          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21101          * @param {Store} this
21102          * @param {Object} meta The JSON metadata
21103          */
21104         metachange : true,
21105         /**
21106          * @event add
21107          * Fires when Records have been added to the Store
21108          * @param {Store} this
21109          * @param {Roo.data.Record[]} records The array of Records added
21110          * @param {Number} index The index at which the record(s) were added
21111          */
21112         add : true,
21113         /**
21114          * @event remove
21115          * Fires when a Record has been removed from the Store
21116          * @param {Store} this
21117          * @param {Roo.data.Record} record The Record that was removed
21118          * @param {Number} index The index at which the record was removed
21119          */
21120         remove : true,
21121         /**
21122          * @event update
21123          * Fires when a Record has been updated
21124          * @param {Store} this
21125          * @param {Roo.data.Record} record The Record that was updated
21126          * @param {String} operation The update operation being performed.  Value may be one of:
21127          * <pre><code>
21128  Roo.data.Record.EDIT
21129  Roo.data.Record.REJECT
21130  Roo.data.Record.COMMIT
21131          * </code></pre>
21132          */
21133         update : true,
21134         /**
21135          * @event clear
21136          * Fires when the data cache has been cleared.
21137          * @param {Store} this
21138          */
21139         clear : true,
21140         /**
21141          * @event beforeload
21142          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21143          * the load action will be canceled.
21144          * @param {Store} this
21145          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21146          */
21147         beforeload : true,
21148         /**
21149          * @event beforeloadadd
21150          * Fires after a new set of Records has been loaded.
21151          * @param {Store} this
21152          * @param {Roo.data.Record[]} records The Records that were loaded
21153          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21154          */
21155         beforeloadadd : true,
21156         /**
21157          * @event load
21158          * Fires after a new set of Records has been loaded, before they are added to the store.
21159          * @param {Store} this
21160          * @param {Roo.data.Record[]} records The Records that were loaded
21161          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21162          * @params {Object} return from reader
21163          */
21164         load : true,
21165         /**
21166          * @event loadexception
21167          * Fires if an exception occurs in the Proxy during loading.
21168          * Called with the signature of the Proxy's "loadexception" event.
21169          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21170          * 
21171          * @param {Proxy} 
21172          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21173          * @param {Object} load options 
21174          * @param {Object} jsonData from your request (normally this contains the Exception)
21175          */
21176         loadexception : true
21177     });
21178     
21179     if(this.proxy){
21180         this.proxy = Roo.factory(this.proxy, Roo.data);
21181         this.proxy.xmodule = this.xmodule || false;
21182         this.relayEvents(this.proxy,  ["loadexception"]);
21183     }
21184     this.sortToggle = {};
21185     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21186
21187     Roo.data.Store.superclass.constructor.call(this);
21188
21189     if(this.inlineData){
21190         this.loadData(this.inlineData);
21191         delete this.inlineData;
21192     }
21193 };
21194
21195 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21196      /**
21197     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21198     * without a remote query - used by combo/forms at present.
21199     */
21200     
21201     /**
21202     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21203     */
21204     /**
21205     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21206     */
21207     /**
21208     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21209     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21210     */
21211     /**
21212     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21213     * on any HTTP request
21214     */
21215     /**
21216     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21217     */
21218     /**
21219     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21220     */
21221     multiSort: false,
21222     /**
21223     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21224     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21225     */
21226     remoteSort : false,
21227
21228     /**
21229     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21230      * loaded or when a record is removed. (defaults to false).
21231     */
21232     pruneModifiedRecords : false,
21233
21234     // private
21235     lastOptions : null,
21236
21237     /**
21238      * Add Records to the Store and fires the add event.
21239      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21240      */
21241     add : function(records){
21242         records = [].concat(records);
21243         for(var i = 0, len = records.length; i < len; i++){
21244             records[i].join(this);
21245         }
21246         var index = this.data.length;
21247         this.data.addAll(records);
21248         this.fireEvent("add", this, records, index);
21249     },
21250
21251     /**
21252      * Remove a Record from the Store and fires the remove event.
21253      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21254      */
21255     remove : function(record){
21256         var index = this.data.indexOf(record);
21257         this.data.removeAt(index);
21258         if(this.pruneModifiedRecords){
21259             this.modified.remove(record);
21260         }
21261         this.fireEvent("remove", this, record, index);
21262     },
21263
21264     /**
21265      * Remove all Records from the Store and fires the clear event.
21266      */
21267     removeAll : function(){
21268         this.data.clear();
21269         if(this.pruneModifiedRecords){
21270             this.modified = [];
21271         }
21272         this.fireEvent("clear", this);
21273     },
21274
21275     /**
21276      * Inserts Records to the Store at the given index and fires the add event.
21277      * @param {Number} index The start index at which to insert the passed Records.
21278      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21279      */
21280     insert : function(index, records){
21281         records = [].concat(records);
21282         for(var i = 0, len = records.length; i < len; i++){
21283             this.data.insert(index, records[i]);
21284             records[i].join(this);
21285         }
21286         this.fireEvent("add", this, records, index);
21287     },
21288
21289     /**
21290      * Get the index within the cache of the passed Record.
21291      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21292      * @return {Number} The index of the passed Record. Returns -1 if not found.
21293      */
21294     indexOf : function(record){
21295         return this.data.indexOf(record);
21296     },
21297
21298     /**
21299      * Get the index within the cache of the Record with the passed id.
21300      * @param {String} id The id of the Record to find.
21301      * @return {Number} The index of the Record. Returns -1 if not found.
21302      */
21303     indexOfId : function(id){
21304         return this.data.indexOfKey(id);
21305     },
21306
21307     /**
21308      * Get the Record with the specified id.
21309      * @param {String} id The id of the Record to find.
21310      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21311      */
21312     getById : function(id){
21313         return this.data.key(id);
21314     },
21315
21316     /**
21317      * Get the Record at the specified index.
21318      * @param {Number} index The index of the Record to find.
21319      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21320      */
21321     getAt : function(index){
21322         return this.data.itemAt(index);
21323     },
21324
21325     /**
21326      * Returns a range of Records between specified indices.
21327      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21328      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21329      * @return {Roo.data.Record[]} An array of Records
21330      */
21331     getRange : function(start, end){
21332         return this.data.getRange(start, end);
21333     },
21334
21335     // private
21336     storeOptions : function(o){
21337         o = Roo.apply({}, o);
21338         delete o.callback;
21339         delete o.scope;
21340         this.lastOptions = o;
21341     },
21342
21343     /**
21344      * Loads the Record cache from the configured Proxy using the configured Reader.
21345      * <p>
21346      * If using remote paging, then the first load call must specify the <em>start</em>
21347      * and <em>limit</em> properties in the options.params property to establish the initial
21348      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21349      * <p>
21350      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21351      * and this call will return before the new data has been loaded. Perform any post-processing
21352      * in a callback function, or in a "load" event handler.</strong>
21353      * <p>
21354      * @param {Object} options An object containing properties which control loading options:<ul>
21355      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21356      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21357      * passed the following arguments:<ul>
21358      * <li>r : Roo.data.Record[]</li>
21359      * <li>options: Options object from the load call</li>
21360      * <li>success: Boolean success indicator</li></ul></li>
21361      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21362      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21363      * </ul>
21364      */
21365     load : function(options){
21366         options = options || {};
21367         if(this.fireEvent("beforeload", this, options) !== false){
21368             this.storeOptions(options);
21369             var p = Roo.apply(options.params || {}, this.baseParams);
21370             // if meta was not loaded from remote source.. try requesting it.
21371             if (!this.reader.metaFromRemote) {
21372                 p._requestMeta = 1;
21373             }
21374             if(this.sortInfo && this.remoteSort){
21375                 var pn = this.paramNames;
21376                 p[pn["sort"]] = this.sortInfo.field;
21377                 p[pn["dir"]] = this.sortInfo.direction;
21378             }
21379             if (this.multiSort) {
21380                 var pn = this.paramNames;
21381                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21382             }
21383             
21384             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21385         }
21386     },
21387
21388     /**
21389      * Reloads the Record cache from the configured Proxy using the configured Reader and
21390      * the options from the last load operation performed.
21391      * @param {Object} options (optional) An object containing properties which may override the options
21392      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21393      * the most recently used options are reused).
21394      */
21395     reload : function(options){
21396         this.load(Roo.applyIf(options||{}, this.lastOptions));
21397     },
21398
21399     // private
21400     // Called as a callback by the Reader during a load operation.
21401     loadRecords : function(o, options, success){
21402         if(!o || success === false){
21403             if(success !== false){
21404                 this.fireEvent("load", this, [], options, o);
21405             }
21406             if(options.callback){
21407                 options.callback.call(options.scope || this, [], options, false);
21408             }
21409             return;
21410         }
21411         // if data returned failure - throw an exception.
21412         if (o.success === false) {
21413             // show a message if no listener is registered.
21414             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21415                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21416             }
21417             // loadmask wil be hooked into this..
21418             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21419             return;
21420         }
21421         var r = o.records, t = o.totalRecords || r.length;
21422         
21423         this.fireEvent("beforeloadadd", this, r, options, o);
21424         
21425         if(!options || options.add !== true){
21426             if(this.pruneModifiedRecords){
21427                 this.modified = [];
21428             }
21429             for(var i = 0, len = r.length; i < len; i++){
21430                 r[i].join(this);
21431             }
21432             if(this.snapshot){
21433                 this.data = this.snapshot;
21434                 delete this.snapshot;
21435             }
21436             this.data.clear();
21437             this.data.addAll(r);
21438             this.totalLength = t;
21439             this.applySort();
21440             this.fireEvent("datachanged", this);
21441         }else{
21442             this.totalLength = Math.max(t, this.data.length+r.length);
21443             this.add(r);
21444         }
21445         this.fireEvent("load", this, r, options, o);
21446         if(options.callback){
21447             options.callback.call(options.scope || this, r, options, true);
21448         }
21449     },
21450
21451
21452     /**
21453      * Loads data from a passed data block. A Reader which understands the format of the data
21454      * must have been configured in the constructor.
21455      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21456      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21457      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21458      */
21459     loadData : function(o, append){
21460         var r = this.reader.readRecords(o);
21461         this.loadRecords(r, {add: append}, true);
21462     },
21463
21464     /**
21465      * Gets the number of cached records.
21466      * <p>
21467      * <em>If using paging, this may not be the total size of the dataset. If the data object
21468      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21469      * the data set size</em>
21470      */
21471     getCount : function(){
21472         return this.data.length || 0;
21473     },
21474
21475     /**
21476      * Gets the total number of records in the dataset as returned by the server.
21477      * <p>
21478      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21479      * the dataset size</em>
21480      */
21481     getTotalCount : function(){
21482         return this.totalLength || 0;
21483     },
21484
21485     /**
21486      * Returns the sort state of the Store as an object with two properties:
21487      * <pre><code>
21488  field {String} The name of the field by which the Records are sorted
21489  direction {String} The sort order, "ASC" or "DESC"
21490      * </code></pre>
21491      */
21492     getSortState : function(){
21493         return this.sortInfo;
21494     },
21495
21496     // private
21497     applySort : function(){
21498         if(this.sortInfo && !this.remoteSort){
21499             var s = this.sortInfo, f = s.field;
21500             var st = this.fields.get(f).sortType;
21501             var fn = function(r1, r2){
21502                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21503                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21504             };
21505             this.data.sort(s.direction, fn);
21506             if(this.snapshot && this.snapshot != this.data){
21507                 this.snapshot.sort(s.direction, fn);
21508             }
21509         }
21510     },
21511
21512     /**
21513      * Sets the default sort column and order to be used by the next load operation.
21514      * @param {String} fieldName The name of the field to sort by.
21515      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21516      */
21517     setDefaultSort : function(field, dir){
21518         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21519     },
21520
21521     /**
21522      * Sort the Records.
21523      * If remote sorting is used, the sort is performed on the server, and the cache is
21524      * reloaded. If local sorting is used, the cache is sorted internally.
21525      * @param {String} fieldName The name of the field to sort by.
21526      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21527      */
21528     sort : function(fieldName, dir){
21529         var f = this.fields.get(fieldName);
21530         if(!dir){
21531             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21532             
21533             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21534                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21535             }else{
21536                 dir = f.sortDir;
21537             }
21538         }
21539         this.sortToggle[f.name] = dir;
21540         this.sortInfo = {field: f.name, direction: dir};
21541         if(!this.remoteSort){
21542             this.applySort();
21543             this.fireEvent("datachanged", this);
21544         }else{
21545             this.load(this.lastOptions);
21546         }
21547     },
21548
21549     /**
21550      * Calls the specified function for each of the Records in the cache.
21551      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21552      * Returning <em>false</em> aborts and exits the iteration.
21553      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21554      */
21555     each : function(fn, scope){
21556         this.data.each(fn, scope);
21557     },
21558
21559     /**
21560      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21561      * (e.g., during paging).
21562      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21563      */
21564     getModifiedRecords : function(){
21565         return this.modified;
21566     },
21567
21568     // private
21569     createFilterFn : function(property, value, anyMatch){
21570         if(!value.exec){ // not a regex
21571             value = String(value);
21572             if(value.length == 0){
21573                 return false;
21574             }
21575             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21576         }
21577         return function(r){
21578             return value.test(r.data[property]);
21579         };
21580     },
21581
21582     /**
21583      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21584      * @param {String} property A field on your records
21585      * @param {Number} start The record index to start at (defaults to 0)
21586      * @param {Number} end The last record index to include (defaults to length - 1)
21587      * @return {Number} The sum
21588      */
21589     sum : function(property, start, end){
21590         var rs = this.data.items, v = 0;
21591         start = start || 0;
21592         end = (end || end === 0) ? end : rs.length-1;
21593
21594         for(var i = start; i <= end; i++){
21595             v += (rs[i].data[property] || 0);
21596         }
21597         return v;
21598     },
21599
21600     /**
21601      * Filter the records by a specified property.
21602      * @param {String} field A field on your records
21603      * @param {String/RegExp} value Either a string that the field
21604      * should start with or a RegExp to test against the field
21605      * @param {Boolean} anyMatch True to match any part not just the beginning
21606      */
21607     filter : function(property, value, anyMatch){
21608         var fn = this.createFilterFn(property, value, anyMatch);
21609         return fn ? this.filterBy(fn) : this.clearFilter();
21610     },
21611
21612     /**
21613      * Filter by a function. The specified function will be called with each
21614      * record in this data source. If the function returns true the record is included,
21615      * otherwise it is filtered.
21616      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21617      * @param {Object} scope (optional) The scope of the function (defaults to this)
21618      */
21619     filterBy : function(fn, scope){
21620         this.snapshot = this.snapshot || this.data;
21621         this.data = this.queryBy(fn, scope||this);
21622         this.fireEvent("datachanged", this);
21623     },
21624
21625     /**
21626      * Query the records by a specified property.
21627      * @param {String} field A field on your records
21628      * @param {String/RegExp} value Either a string that the field
21629      * should start with or a RegExp to test against the field
21630      * @param {Boolean} anyMatch True to match any part not just the beginning
21631      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21632      */
21633     query : function(property, value, anyMatch){
21634         var fn = this.createFilterFn(property, value, anyMatch);
21635         return fn ? this.queryBy(fn) : this.data.clone();
21636     },
21637
21638     /**
21639      * Query by a function. The specified function will be called with each
21640      * record in this data source. If the function returns true the record is included
21641      * in the results.
21642      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21643      * @param {Object} scope (optional) The scope of the function (defaults to this)
21644       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21645      **/
21646     queryBy : function(fn, scope){
21647         var data = this.snapshot || this.data;
21648         return data.filterBy(fn, scope||this);
21649     },
21650
21651     /**
21652      * Collects unique values for a particular dataIndex from this store.
21653      * @param {String} dataIndex The property to collect
21654      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21655      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21656      * @return {Array} An array of the unique values
21657      **/
21658     collect : function(dataIndex, allowNull, bypassFilter){
21659         var d = (bypassFilter === true && this.snapshot) ?
21660                 this.snapshot.items : this.data.items;
21661         var v, sv, r = [], l = {};
21662         for(var i = 0, len = d.length; i < len; i++){
21663             v = d[i].data[dataIndex];
21664             sv = String(v);
21665             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21666                 l[sv] = true;
21667                 r[r.length] = v;
21668             }
21669         }
21670         return r;
21671     },
21672
21673     /**
21674      * Revert to a view of the Record cache with no filtering applied.
21675      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21676      */
21677     clearFilter : function(suppressEvent){
21678         if(this.snapshot && this.snapshot != this.data){
21679             this.data = this.snapshot;
21680             delete this.snapshot;
21681             if(suppressEvent !== true){
21682                 this.fireEvent("datachanged", this);
21683             }
21684         }
21685     },
21686
21687     // private
21688     afterEdit : function(record){
21689         if(this.modified.indexOf(record) == -1){
21690             this.modified.push(record);
21691         }
21692         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21693     },
21694     
21695     // private
21696     afterReject : function(record){
21697         this.modified.remove(record);
21698         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21699     },
21700
21701     // private
21702     afterCommit : function(record){
21703         this.modified.remove(record);
21704         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21705     },
21706
21707     /**
21708      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21709      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21710      */
21711     commitChanges : function(){
21712         var m = this.modified.slice(0);
21713         this.modified = [];
21714         for(var i = 0, len = m.length; i < len; i++){
21715             m[i].commit();
21716         }
21717     },
21718
21719     /**
21720      * Cancel outstanding changes on all changed records.
21721      */
21722     rejectChanges : function(){
21723         var m = this.modified.slice(0);
21724         this.modified = [];
21725         for(var i = 0, len = m.length; i < len; i++){
21726             m[i].reject();
21727         }
21728     },
21729
21730     onMetaChange : function(meta, rtype, o){
21731         this.recordType = rtype;
21732         this.fields = rtype.prototype.fields;
21733         delete this.snapshot;
21734         this.sortInfo = meta.sortInfo || this.sortInfo;
21735         this.modified = [];
21736         this.fireEvent('metachange', this, this.reader.meta);
21737     }
21738 });/*
21739  * Based on:
21740  * Ext JS Library 1.1.1
21741  * Copyright(c) 2006-2007, Ext JS, LLC.
21742  *
21743  * Originally Released Under LGPL - original licence link has changed is not relivant.
21744  *
21745  * Fork - LGPL
21746  * <script type="text/javascript">
21747  */
21748
21749 /**
21750  * @class Roo.data.SimpleStore
21751  * @extends Roo.data.Store
21752  * Small helper class to make creating Stores from Array data easier.
21753  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21754  * @cfg {Array} fields An array of field definition objects, or field name strings.
21755  * @cfg {Array} data The multi-dimensional array of data
21756  * @constructor
21757  * @param {Object} config
21758  */
21759 Roo.data.SimpleStore = function(config){
21760     Roo.data.SimpleStore.superclass.constructor.call(this, {
21761         isLocal : true,
21762         reader: new Roo.data.ArrayReader({
21763                 id: config.id
21764             },
21765             Roo.data.Record.create(config.fields)
21766         ),
21767         proxy : new Roo.data.MemoryProxy(config.data)
21768     });
21769     this.load();
21770 };
21771 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21772  * Based on:
21773  * Ext JS Library 1.1.1
21774  * Copyright(c) 2006-2007, Ext JS, LLC.
21775  *
21776  * Originally Released Under LGPL - original licence link has changed is not relivant.
21777  *
21778  * Fork - LGPL
21779  * <script type="text/javascript">
21780  */
21781
21782 /**
21783 /**
21784  * @extends Roo.data.Store
21785  * @class Roo.data.JsonStore
21786  * Small helper class to make creating Stores for JSON data easier. <br/>
21787 <pre><code>
21788 var store = new Roo.data.JsonStore({
21789     url: 'get-images.php',
21790     root: 'images',
21791     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21792 });
21793 </code></pre>
21794  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21795  * JsonReader and HttpProxy (unless inline data is provided).</b>
21796  * @cfg {Array} fields An array of field definition objects, or field name strings.
21797  * @constructor
21798  * @param {Object} config
21799  */
21800 Roo.data.JsonStore = function(c){
21801     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21802         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21803         reader: new Roo.data.JsonReader(c, c.fields)
21804     }));
21805 };
21806 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21807  * Based on:
21808  * Ext JS Library 1.1.1
21809  * Copyright(c) 2006-2007, Ext JS, LLC.
21810  *
21811  * Originally Released Under LGPL - original licence link has changed is not relivant.
21812  *
21813  * Fork - LGPL
21814  * <script type="text/javascript">
21815  */
21816
21817  
21818 Roo.data.Field = function(config){
21819     if(typeof config == "string"){
21820         config = {name: config};
21821     }
21822     Roo.apply(this, config);
21823     
21824     if(!this.type){
21825         this.type = "auto";
21826     }
21827     
21828     var st = Roo.data.SortTypes;
21829     // named sortTypes are supported, here we look them up
21830     if(typeof this.sortType == "string"){
21831         this.sortType = st[this.sortType];
21832     }
21833     
21834     // set default sortType for strings and dates
21835     if(!this.sortType){
21836         switch(this.type){
21837             case "string":
21838                 this.sortType = st.asUCString;
21839                 break;
21840             case "date":
21841                 this.sortType = st.asDate;
21842                 break;
21843             default:
21844                 this.sortType = st.none;
21845         }
21846     }
21847
21848     // define once
21849     var stripRe = /[\$,%]/g;
21850
21851     // prebuilt conversion function for this field, instead of
21852     // switching every time we're reading a value
21853     if(!this.convert){
21854         var cv, dateFormat = this.dateFormat;
21855         switch(this.type){
21856             case "":
21857             case "auto":
21858             case undefined:
21859                 cv = function(v){ return v; };
21860                 break;
21861             case "string":
21862                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21863                 break;
21864             case "int":
21865                 cv = function(v){
21866                     return v !== undefined && v !== null && v !== '' ?
21867                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21868                     };
21869                 break;
21870             case "float":
21871                 cv = function(v){
21872                     return v !== undefined && v !== null && v !== '' ?
21873                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21874                     };
21875                 break;
21876             case "bool":
21877             case "boolean":
21878                 cv = function(v){ return v === true || v === "true" || v == 1; };
21879                 break;
21880             case "date":
21881                 cv = function(v){
21882                     if(!v){
21883                         return '';
21884                     }
21885                     if(v instanceof Date){
21886                         return v;
21887                     }
21888                     if(dateFormat){
21889                         if(dateFormat == "timestamp"){
21890                             return new Date(v*1000);
21891                         }
21892                         return Date.parseDate(v, dateFormat);
21893                     }
21894                     var parsed = Date.parse(v);
21895                     return parsed ? new Date(parsed) : null;
21896                 };
21897              break;
21898             
21899         }
21900         this.convert = cv;
21901     }
21902 };
21903
21904 Roo.data.Field.prototype = {
21905     dateFormat: null,
21906     defaultValue: "",
21907     mapping: null,
21908     sortType : null,
21909     sortDir : "ASC"
21910 };/*
21911  * Based on:
21912  * Ext JS Library 1.1.1
21913  * Copyright(c) 2006-2007, Ext JS, LLC.
21914  *
21915  * Originally Released Under LGPL - original licence link has changed is not relivant.
21916  *
21917  * Fork - LGPL
21918  * <script type="text/javascript">
21919  */
21920  
21921 // Base class for reading structured data from a data source.  This class is intended to be
21922 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21923
21924 /**
21925  * @class Roo.data.DataReader
21926  * Base class for reading structured data from a data source.  This class is intended to be
21927  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21928  */
21929
21930 Roo.data.DataReader = function(meta, recordType){
21931     
21932     this.meta = meta;
21933     
21934     this.recordType = recordType instanceof Array ? 
21935         Roo.data.Record.create(recordType) : recordType;
21936 };
21937
21938 Roo.data.DataReader.prototype = {
21939      /**
21940      * Create an empty record
21941      * @param {Object} data (optional) - overlay some values
21942      * @return {Roo.data.Record} record created.
21943      */
21944     newRow :  function(d) {
21945         var da =  {};
21946         this.recordType.prototype.fields.each(function(c) {
21947             switch( c.type) {
21948                 case 'int' : da[c.name] = 0; break;
21949                 case 'date' : da[c.name] = new Date(); break;
21950                 case 'float' : da[c.name] = 0.0; break;
21951                 case 'boolean' : da[c.name] = false; break;
21952                 default : da[c.name] = ""; break;
21953             }
21954             
21955         });
21956         return new this.recordType(Roo.apply(da, d));
21957     }
21958     
21959 };/*
21960  * Based on:
21961  * Ext JS Library 1.1.1
21962  * Copyright(c) 2006-2007, Ext JS, LLC.
21963  *
21964  * Originally Released Under LGPL - original licence link has changed is not relivant.
21965  *
21966  * Fork - LGPL
21967  * <script type="text/javascript">
21968  */
21969
21970 /**
21971  * @class Roo.data.DataProxy
21972  * @extends Roo.data.Observable
21973  * This class is an abstract base class for implementations which provide retrieval of
21974  * unformatted data objects.<br>
21975  * <p>
21976  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21977  * (of the appropriate type which knows how to parse the data object) to provide a block of
21978  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21979  * <p>
21980  * Custom implementations must implement the load method as described in
21981  * {@link Roo.data.HttpProxy#load}.
21982  */
21983 Roo.data.DataProxy = function(){
21984     this.addEvents({
21985         /**
21986          * @event beforeload
21987          * Fires before a network request is made to retrieve a data object.
21988          * @param {Object} This DataProxy object.
21989          * @param {Object} params The params parameter to the load function.
21990          */
21991         beforeload : true,
21992         /**
21993          * @event load
21994          * Fires before the load method's callback is called.
21995          * @param {Object} This DataProxy object.
21996          * @param {Object} o The data object.
21997          * @param {Object} arg The callback argument object passed to the load function.
21998          */
21999         load : true,
22000         /**
22001          * @event loadexception
22002          * Fires if an Exception occurs during data retrieval.
22003          * @param {Object} This DataProxy object.
22004          * @param {Object} o The data object.
22005          * @param {Object} arg The callback argument object passed to the load function.
22006          * @param {Object} e The Exception.
22007          */
22008         loadexception : true
22009     });
22010     Roo.data.DataProxy.superclass.constructor.call(this);
22011 };
22012
22013 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22014
22015     /**
22016      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22017      */
22018 /*
22019  * Based on:
22020  * Ext JS Library 1.1.1
22021  * Copyright(c) 2006-2007, Ext JS, LLC.
22022  *
22023  * Originally Released Under LGPL - original licence link has changed is not relivant.
22024  *
22025  * Fork - LGPL
22026  * <script type="text/javascript">
22027  */
22028 /**
22029  * @class Roo.data.MemoryProxy
22030  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22031  * to the Reader when its load method is called.
22032  * @constructor
22033  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22034  */
22035 Roo.data.MemoryProxy = function(data){
22036     if (data.data) {
22037         data = data.data;
22038     }
22039     Roo.data.MemoryProxy.superclass.constructor.call(this);
22040     this.data = data;
22041 };
22042
22043 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22044     /**
22045      * Load data from the requested source (in this case an in-memory
22046      * data object passed to the constructor), read the data object into
22047      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22048      * process that block using the passed callback.
22049      * @param {Object} params This parameter is not used by the MemoryProxy class.
22050      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22051      * object into a block of Roo.data.Records.
22052      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22053      * The function must be passed <ul>
22054      * <li>The Record block object</li>
22055      * <li>The "arg" argument from the load function</li>
22056      * <li>A boolean success indicator</li>
22057      * </ul>
22058      * @param {Object} scope The scope in which to call the callback
22059      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22060      */
22061     load : function(params, reader, callback, scope, arg){
22062         params = params || {};
22063         var result;
22064         try {
22065             result = reader.readRecords(this.data);
22066         }catch(e){
22067             this.fireEvent("loadexception", this, arg, null, e);
22068             callback.call(scope, null, arg, false);
22069             return;
22070         }
22071         callback.call(scope, result, arg, true);
22072     },
22073     
22074     // private
22075     update : function(params, records){
22076         
22077     }
22078 });/*
22079  * Based on:
22080  * Ext JS Library 1.1.1
22081  * Copyright(c) 2006-2007, Ext JS, LLC.
22082  *
22083  * Originally Released Under LGPL - original licence link has changed is not relivant.
22084  *
22085  * Fork - LGPL
22086  * <script type="text/javascript">
22087  */
22088 /**
22089  * @class Roo.data.HttpProxy
22090  * @extends Roo.data.DataProxy
22091  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22092  * configured to reference a certain URL.<br><br>
22093  * <p>
22094  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22095  * from which the running page was served.<br><br>
22096  * <p>
22097  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22098  * <p>
22099  * Be aware that to enable the browser to parse an XML document, the server must set
22100  * the Content-Type header in the HTTP response to "text/xml".
22101  * @constructor
22102  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22103  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22104  * will be used to make the request.
22105  */
22106 Roo.data.HttpProxy = function(conn){
22107     Roo.data.HttpProxy.superclass.constructor.call(this);
22108     // is conn a conn config or a real conn?
22109     this.conn = conn;
22110     this.useAjax = !conn || !conn.events;
22111   
22112 };
22113
22114 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22115     // thse are take from connection...
22116     
22117     /**
22118      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22119      */
22120     /**
22121      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22122      * extra parameters to each request made by this object. (defaults to undefined)
22123      */
22124     /**
22125      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22126      *  to each request made by this object. (defaults to undefined)
22127      */
22128     /**
22129      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
22130      */
22131     /**
22132      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22133      */
22134      /**
22135      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22136      * @type Boolean
22137      */
22138   
22139
22140     /**
22141      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22142      * @type Boolean
22143      */
22144     /**
22145      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22146      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22147      * a finer-grained basis than the DataProxy events.
22148      */
22149     getConnection : function(){
22150         return this.useAjax ? Roo.Ajax : this.conn;
22151     },
22152
22153     /**
22154      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22155      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22156      * process that block using the passed callback.
22157      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22158      * for the request to the remote server.
22159      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22160      * object into a block of Roo.data.Records.
22161      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22162      * The function must be passed <ul>
22163      * <li>The Record block object</li>
22164      * <li>The "arg" argument from the load function</li>
22165      * <li>A boolean success indicator</li>
22166      * </ul>
22167      * @param {Object} scope The scope in which to call the callback
22168      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22169      */
22170     load : function(params, reader, callback, scope, arg){
22171         if(this.fireEvent("beforeload", this, params) !== false){
22172             var  o = {
22173                 params : params || {},
22174                 request: {
22175                     callback : callback,
22176                     scope : scope,
22177                     arg : arg
22178                 },
22179                 reader: reader,
22180                 callback : this.loadResponse,
22181                 scope: this
22182             };
22183             if(this.useAjax){
22184                 Roo.applyIf(o, this.conn);
22185                 if(this.activeRequest){
22186                     Roo.Ajax.abort(this.activeRequest);
22187                 }
22188                 this.activeRequest = Roo.Ajax.request(o);
22189             }else{
22190                 this.conn.request(o);
22191             }
22192         }else{
22193             callback.call(scope||this, null, arg, false);
22194         }
22195     },
22196
22197     // private
22198     loadResponse : function(o, success, response){
22199         delete this.activeRequest;
22200         if(!success){
22201             this.fireEvent("loadexception", this, o, response);
22202             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22203             return;
22204         }
22205         var result;
22206         try {
22207             result = o.reader.read(response);
22208         }catch(e){
22209             this.fireEvent("loadexception", this, o, response, e);
22210             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22211             return;
22212         }
22213         
22214         this.fireEvent("load", this, o, o.request.arg);
22215         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22216     },
22217
22218     // private
22219     update : function(dataSet){
22220
22221     },
22222
22223     // private
22224     updateResponse : function(dataSet){
22225
22226     }
22227 });/*
22228  * Based on:
22229  * Ext JS Library 1.1.1
22230  * Copyright(c) 2006-2007, Ext JS, LLC.
22231  *
22232  * Originally Released Under LGPL - original licence link has changed is not relivant.
22233  *
22234  * Fork - LGPL
22235  * <script type="text/javascript">
22236  */
22237
22238 /**
22239  * @class Roo.data.ScriptTagProxy
22240  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22241  * other than the originating domain of the running page.<br><br>
22242  * <p>
22243  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
22244  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22245  * <p>
22246  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22247  * source code that is used as the source inside a &lt;script> tag.<br><br>
22248  * <p>
22249  * In order for the browser to process the returned data, the server must wrap the data object
22250  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22251  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22252  * depending on whether the callback name was passed:
22253  * <p>
22254  * <pre><code>
22255 boolean scriptTag = false;
22256 String cb = request.getParameter("callback");
22257 if (cb != null) {
22258     scriptTag = true;
22259     response.setContentType("text/javascript");
22260 } else {
22261     response.setContentType("application/x-json");
22262 }
22263 Writer out = response.getWriter();
22264 if (scriptTag) {
22265     out.write(cb + "(");
22266 }
22267 out.print(dataBlock.toJsonString());
22268 if (scriptTag) {
22269     out.write(");");
22270 }
22271 </pre></code>
22272  *
22273  * @constructor
22274  * @param {Object} config A configuration object.
22275  */
22276 Roo.data.ScriptTagProxy = function(config){
22277     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22278     Roo.apply(this, config);
22279     this.head = document.getElementsByTagName("head")[0];
22280 };
22281
22282 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22283
22284 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22285     /**
22286      * @cfg {String} url The URL from which to request the data object.
22287      */
22288     /**
22289      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22290      */
22291     timeout : 30000,
22292     /**
22293      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22294      * the server the name of the callback function set up by the load call to process the returned data object.
22295      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22296      * javascript output which calls this named function passing the data object as its only parameter.
22297      */
22298     callbackParam : "callback",
22299     /**
22300      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22301      * name to the request.
22302      */
22303     nocache : true,
22304
22305     /**
22306      * Load data from the configured URL, read the data object into
22307      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22308      * process that block using the passed callback.
22309      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22310      * for the request to the remote server.
22311      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22312      * object into a block of Roo.data.Records.
22313      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22314      * The function must be passed <ul>
22315      * <li>The Record block object</li>
22316      * <li>The "arg" argument from the load function</li>
22317      * <li>A boolean success indicator</li>
22318      * </ul>
22319      * @param {Object} scope The scope in which to call the callback
22320      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22321      */
22322     load : function(params, reader, callback, scope, arg){
22323         if(this.fireEvent("beforeload", this, params) !== false){
22324
22325             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22326
22327             var url = this.url;
22328             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22329             if(this.nocache){
22330                 url += "&_dc=" + (new Date().getTime());
22331             }
22332             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22333             var trans = {
22334                 id : transId,
22335                 cb : "stcCallback"+transId,
22336                 scriptId : "stcScript"+transId,
22337                 params : params,
22338                 arg : arg,
22339                 url : url,
22340                 callback : callback,
22341                 scope : scope,
22342                 reader : reader
22343             };
22344             var conn = this;
22345
22346             window[trans.cb] = function(o){
22347                 conn.handleResponse(o, trans);
22348             };
22349
22350             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22351
22352             if(this.autoAbort !== false){
22353                 this.abort();
22354             }
22355
22356             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22357
22358             var script = document.createElement("script");
22359             script.setAttribute("src", url);
22360             script.setAttribute("type", "text/javascript");
22361             script.setAttribute("id", trans.scriptId);
22362             this.head.appendChild(script);
22363
22364             this.trans = trans;
22365         }else{
22366             callback.call(scope||this, null, arg, false);
22367         }
22368     },
22369
22370     // private
22371     isLoading : function(){
22372         return this.trans ? true : false;
22373     },
22374
22375     /**
22376      * Abort the current server request.
22377      */
22378     abort : function(){
22379         if(this.isLoading()){
22380             this.destroyTrans(this.trans);
22381         }
22382     },
22383
22384     // private
22385     destroyTrans : function(trans, isLoaded){
22386         this.head.removeChild(document.getElementById(trans.scriptId));
22387         clearTimeout(trans.timeoutId);
22388         if(isLoaded){
22389             window[trans.cb] = undefined;
22390             try{
22391                 delete window[trans.cb];
22392             }catch(e){}
22393         }else{
22394             // if hasn't been loaded, wait for load to remove it to prevent script error
22395             window[trans.cb] = function(){
22396                 window[trans.cb] = undefined;
22397                 try{
22398                     delete window[trans.cb];
22399                 }catch(e){}
22400             };
22401         }
22402     },
22403
22404     // private
22405     handleResponse : function(o, trans){
22406         this.trans = false;
22407         this.destroyTrans(trans, true);
22408         var result;
22409         try {
22410             result = trans.reader.readRecords(o);
22411         }catch(e){
22412             this.fireEvent("loadexception", this, o, trans.arg, e);
22413             trans.callback.call(trans.scope||window, null, trans.arg, false);
22414             return;
22415         }
22416         this.fireEvent("load", this, o, trans.arg);
22417         trans.callback.call(trans.scope||window, result, trans.arg, true);
22418     },
22419
22420     // private
22421     handleFailure : function(trans){
22422         this.trans = false;
22423         this.destroyTrans(trans, false);
22424         this.fireEvent("loadexception", this, null, trans.arg);
22425         trans.callback.call(trans.scope||window, null, trans.arg, false);
22426     }
22427 });/*
22428  * Based on:
22429  * Ext JS Library 1.1.1
22430  * Copyright(c) 2006-2007, Ext JS, LLC.
22431  *
22432  * Originally Released Under LGPL - original licence link has changed is not relivant.
22433  *
22434  * Fork - LGPL
22435  * <script type="text/javascript">
22436  */
22437
22438 /**
22439  * @class Roo.data.JsonReader
22440  * @extends Roo.data.DataReader
22441  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22442  * based on mappings in a provided Roo.data.Record constructor.
22443  * 
22444  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22445  * in the reply previously. 
22446  * 
22447  * <p>
22448  * Example code:
22449  * <pre><code>
22450 var RecordDef = Roo.data.Record.create([
22451     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22452     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22453 ]);
22454 var myReader = new Roo.data.JsonReader({
22455     totalProperty: "results",    // The property which contains the total dataset size (optional)
22456     root: "rows",                // The property which contains an Array of row objects
22457     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22458 }, RecordDef);
22459 </code></pre>
22460  * <p>
22461  * This would consume a JSON file like this:
22462  * <pre><code>
22463 { 'results': 2, 'rows': [
22464     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22465     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22466 }
22467 </code></pre>
22468  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22469  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22470  * paged from the remote server.
22471  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22472  * @cfg {String} root name of the property which contains the Array of row objects.
22473  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22474  * @constructor
22475  * Create a new JsonReader
22476  * @param {Object} meta Metadata configuration options
22477  * @param {Object} recordType Either an Array of field definition objects,
22478  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22479  */
22480 Roo.data.JsonReader = function(meta, recordType){
22481     
22482     meta = meta || {};
22483     // set some defaults:
22484     Roo.applyIf(meta, {
22485         totalProperty: 'total',
22486         successProperty : 'success',
22487         root : 'data',
22488         id : 'id'
22489     });
22490     
22491     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22492 };
22493 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22494     
22495     /**
22496      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22497      * Used by Store query builder to append _requestMeta to params.
22498      * 
22499      */
22500     metaFromRemote : false,
22501     /**
22502      * This method is only used by a DataProxy which has retrieved data from a remote server.
22503      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22504      * @return {Object} data A data block which is used by an Roo.data.Store object as
22505      * a cache of Roo.data.Records.
22506      */
22507     read : function(response){
22508         var json = response.responseText;
22509        
22510         var o = /* eval:var:o */ eval("("+json+")");
22511         if(!o) {
22512             throw {message: "JsonReader.read: Json object not found"};
22513         }
22514         
22515         if(o.metaData){
22516             
22517             delete this.ef;
22518             this.metaFromRemote = true;
22519             this.meta = o.metaData;
22520             this.recordType = Roo.data.Record.create(o.metaData.fields);
22521             this.onMetaChange(this.meta, this.recordType, o);
22522         }
22523         return this.readRecords(o);
22524     },
22525
22526     // private function a store will implement
22527     onMetaChange : function(meta, recordType, o){
22528
22529     },
22530
22531     /**
22532          * @ignore
22533          */
22534     simpleAccess: function(obj, subsc) {
22535         return obj[subsc];
22536     },
22537
22538         /**
22539          * @ignore
22540          */
22541     getJsonAccessor: function(){
22542         var re = /[\[\.]/;
22543         return function(expr) {
22544             try {
22545                 return(re.test(expr))
22546                     ? new Function("obj", "return obj." + expr)
22547                     : function(obj){
22548                         return obj[expr];
22549                     };
22550             } catch(e){}
22551             return Roo.emptyFn;
22552         };
22553     }(),
22554
22555     /**
22556      * Create a data block containing Roo.data.Records from an XML document.
22557      * @param {Object} o An object which contains an Array of row objects in the property specified
22558      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22559      * which contains the total size of the dataset.
22560      * @return {Object} data A data block which is used by an Roo.data.Store object as
22561      * a cache of Roo.data.Records.
22562      */
22563     readRecords : function(o){
22564         /**
22565          * After any data loads, the raw JSON data is available for further custom processing.
22566          * @type Object
22567          */
22568         this.o = o;
22569         var s = this.meta, Record = this.recordType,
22570             f = Record.prototype.fields, fi = f.items, fl = f.length;
22571
22572 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22573         if (!this.ef) {
22574             if(s.totalProperty) {
22575                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22576                 }
22577                 if(s.successProperty) {
22578                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22579                 }
22580                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22581                 if (s.id) {
22582                         var g = this.getJsonAccessor(s.id);
22583                         this.getId = function(rec) {
22584                                 var r = g(rec);
22585                                 return (r === undefined || r === "") ? null : r;
22586                         };
22587                 } else {
22588                         this.getId = function(){return null;};
22589                 }
22590             this.ef = [];
22591             for(var jj = 0; jj < fl; jj++){
22592                 f = fi[jj];
22593                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22594                 this.ef[jj] = this.getJsonAccessor(map);
22595             }
22596         }
22597
22598         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22599         if(s.totalProperty){
22600             var vt = parseInt(this.getTotal(o), 10);
22601             if(!isNaN(vt)){
22602                 totalRecords = vt;
22603             }
22604         }
22605         if(s.successProperty){
22606             var vs = this.getSuccess(o);
22607             if(vs === false || vs === 'false'){
22608                 success = false;
22609             }
22610         }
22611         var records = [];
22612             for(var i = 0; i < c; i++){
22613                     var n = root[i];
22614                 var values = {};
22615                 var id = this.getId(n);
22616                 for(var j = 0; j < fl; j++){
22617                     f = fi[j];
22618                 var v = this.ef[j](n);
22619                 if (!f.convert) {
22620                     Roo.log('missing convert for ' + f.name);
22621                     Roo.log(f);
22622                     continue;
22623                 }
22624                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22625                 }
22626                 var record = new Record(values, id);
22627                 record.json = n;
22628                 records[i] = record;
22629             }
22630             return {
22631             raw : o,
22632                 success : success,
22633                 records : records,
22634                 totalRecords : totalRecords
22635             };
22636     }
22637 });/*
22638  * Based on:
22639  * Ext JS Library 1.1.1
22640  * Copyright(c) 2006-2007, Ext JS, LLC.
22641  *
22642  * Originally Released Under LGPL - original licence link has changed is not relivant.
22643  *
22644  * Fork - LGPL
22645  * <script type="text/javascript">
22646  */
22647
22648 /**
22649  * @class Roo.data.XmlReader
22650  * @extends Roo.data.DataReader
22651  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22652  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22653  * <p>
22654  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22655  * header in the HTTP response must be set to "text/xml".</em>
22656  * <p>
22657  * Example code:
22658  * <pre><code>
22659 var RecordDef = Roo.data.Record.create([
22660    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22661    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22662 ]);
22663 var myReader = new Roo.data.XmlReader({
22664    totalRecords: "results", // The element which contains the total dataset size (optional)
22665    record: "row",           // The repeated element which contains row information
22666    id: "id"                 // The element within the row that provides an ID for the record (optional)
22667 }, RecordDef);
22668 </code></pre>
22669  * <p>
22670  * This would consume an XML file like this:
22671  * <pre><code>
22672 &lt;?xml?>
22673 &lt;dataset>
22674  &lt;results>2&lt;/results>
22675  &lt;row>
22676    &lt;id>1&lt;/id>
22677    &lt;name>Bill&lt;/name>
22678    &lt;occupation>Gardener&lt;/occupation>
22679  &lt;/row>
22680  &lt;row>
22681    &lt;id>2&lt;/id>
22682    &lt;name>Ben&lt;/name>
22683    &lt;occupation>Horticulturalist&lt;/occupation>
22684  &lt;/row>
22685 &lt;/dataset>
22686 </code></pre>
22687  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22688  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22689  * paged from the remote server.
22690  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22691  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22692  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22693  * a record identifier value.
22694  * @constructor
22695  * Create a new XmlReader
22696  * @param {Object} meta Metadata configuration options
22697  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22698  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22699  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22700  */
22701 Roo.data.XmlReader = function(meta, recordType){
22702     meta = meta || {};
22703     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22704 };
22705 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22706     /**
22707      * This method is only used by a DataProxy which has retrieved data from a remote server.
22708          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22709          * to contain a method called 'responseXML' that returns an XML document object.
22710      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22711      * a cache of Roo.data.Records.
22712      */
22713     read : function(response){
22714         var doc = response.responseXML;
22715         if(!doc) {
22716             throw {message: "XmlReader.read: XML Document not available"};
22717         }
22718         return this.readRecords(doc);
22719     },
22720
22721     /**
22722      * Create a data block containing Roo.data.Records from an XML document.
22723          * @param {Object} doc A parsed XML document.
22724      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22725      * a cache of Roo.data.Records.
22726      */
22727     readRecords : function(doc){
22728         /**
22729          * After any data loads/reads, the raw XML Document is available for further custom processing.
22730          * @type XMLDocument
22731          */
22732         this.xmlData = doc;
22733         var root = doc.documentElement || doc;
22734         var q = Roo.DomQuery;
22735         var recordType = this.recordType, fields = recordType.prototype.fields;
22736         var sid = this.meta.id;
22737         var totalRecords = 0, success = true;
22738         if(this.meta.totalRecords){
22739             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22740         }
22741         
22742         if(this.meta.success){
22743             var sv = q.selectValue(this.meta.success, root, true);
22744             success = sv !== false && sv !== 'false';
22745         }
22746         var records = [];
22747         var ns = q.select(this.meta.record, root);
22748         for(var i = 0, len = ns.length; i < len; i++) {
22749                 var n = ns[i];
22750                 var values = {};
22751                 var id = sid ? q.selectValue(sid, n) : undefined;
22752                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22753                     var f = fields.items[j];
22754                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22755                     v = f.convert(v);
22756                     values[f.name] = v;
22757                 }
22758                 var record = new recordType(values, id);
22759                 record.node = n;
22760                 records[records.length] = record;
22761             }
22762
22763             return {
22764                 success : success,
22765                 records : records,
22766                 totalRecords : totalRecords || records.length
22767             };
22768     }
22769 });/*
22770  * Based on:
22771  * Ext JS Library 1.1.1
22772  * Copyright(c) 2006-2007, Ext JS, LLC.
22773  *
22774  * Originally Released Under LGPL - original licence link has changed is not relivant.
22775  *
22776  * Fork - LGPL
22777  * <script type="text/javascript">
22778  */
22779
22780 /**
22781  * @class Roo.data.ArrayReader
22782  * @extends Roo.data.DataReader
22783  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22784  * Each element of that Array represents a row of data fields. The
22785  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22786  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22787  * <p>
22788  * Example code:.
22789  * <pre><code>
22790 var RecordDef = Roo.data.Record.create([
22791     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22792     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22793 ]);
22794 var myReader = new Roo.data.ArrayReader({
22795     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22796 }, RecordDef);
22797 </code></pre>
22798  * <p>
22799  * This would consume an Array like this:
22800  * <pre><code>
22801 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22802   </code></pre>
22803  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22804  * @constructor
22805  * Create a new JsonReader
22806  * @param {Object} meta Metadata configuration options.
22807  * @param {Object} recordType Either an Array of field definition objects
22808  * as specified to {@link Roo.data.Record#create},
22809  * or an {@link Roo.data.Record} object
22810  * created using {@link Roo.data.Record#create}.
22811  */
22812 Roo.data.ArrayReader = function(meta, recordType){
22813     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22814 };
22815
22816 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22817     /**
22818      * Create a data block containing Roo.data.Records from an XML document.
22819      * @param {Object} o An Array of row objects which represents the dataset.
22820      * @return {Object} data A data block which is used by an Roo.data.Store object as
22821      * a cache of Roo.data.Records.
22822      */
22823     readRecords : function(o){
22824         var sid = this.meta ? this.meta.id : null;
22825         var recordType = this.recordType, fields = recordType.prototype.fields;
22826         var records = [];
22827         var root = o;
22828             for(var i = 0; i < root.length; i++){
22829                     var n = root[i];
22830                 var values = {};
22831                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22832                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22833                 var f = fields.items[j];
22834                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22835                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22836                 v = f.convert(v);
22837                 values[f.name] = v;
22838             }
22839                 var record = new recordType(values, id);
22840                 record.json = n;
22841                 records[records.length] = record;
22842             }
22843             return {
22844                 records : records,
22845                 totalRecords : records.length
22846             };
22847     }
22848 });/*
22849  * Based on:
22850  * Ext JS Library 1.1.1
22851  * Copyright(c) 2006-2007, Ext JS, LLC.
22852  *
22853  * Originally Released Under LGPL - original licence link has changed is not relivant.
22854  *
22855  * Fork - LGPL
22856  * <script type="text/javascript">
22857  */
22858
22859
22860 /**
22861  * @class Roo.data.Tree
22862  * @extends Roo.util.Observable
22863  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22864  * in the tree have most standard DOM functionality.
22865  * @constructor
22866  * @param {Node} root (optional) The root node
22867  */
22868 Roo.data.Tree = function(root){
22869    this.nodeHash = {};
22870    /**
22871     * The root node for this tree
22872     * @type Node
22873     */
22874    this.root = null;
22875    if(root){
22876        this.setRootNode(root);
22877    }
22878    this.addEvents({
22879        /**
22880         * @event append
22881         * Fires when a new child node is appended to a node in this tree.
22882         * @param {Tree} tree The owner tree
22883         * @param {Node} parent The parent node
22884         * @param {Node} node The newly appended node
22885         * @param {Number} index The index of the newly appended node
22886         */
22887        "append" : true,
22888        /**
22889         * @event remove
22890         * Fires when a child node is removed from a node in this tree.
22891         * @param {Tree} tree The owner tree
22892         * @param {Node} parent The parent node
22893         * @param {Node} node The child node removed
22894         */
22895        "remove" : true,
22896        /**
22897         * @event move
22898         * Fires when a node is moved to a new location in the tree
22899         * @param {Tree} tree The owner tree
22900         * @param {Node} node The node moved
22901         * @param {Node} oldParent The old parent of this node
22902         * @param {Node} newParent The new parent of this node
22903         * @param {Number} index The index it was moved to
22904         */
22905        "move" : true,
22906        /**
22907         * @event insert
22908         * Fires when a new child node is inserted in a node in this tree.
22909         * @param {Tree} tree The owner tree
22910         * @param {Node} parent The parent node
22911         * @param {Node} node The child node inserted
22912         * @param {Node} refNode The child node the node was inserted before
22913         */
22914        "insert" : true,
22915        /**
22916         * @event beforeappend
22917         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22918         * @param {Tree} tree The owner tree
22919         * @param {Node} parent The parent node
22920         * @param {Node} node The child node to be appended
22921         */
22922        "beforeappend" : true,
22923        /**
22924         * @event beforeremove
22925         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22926         * @param {Tree} tree The owner tree
22927         * @param {Node} parent The parent node
22928         * @param {Node} node The child node to be removed
22929         */
22930        "beforeremove" : true,
22931        /**
22932         * @event beforemove
22933         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22934         * @param {Tree} tree The owner tree
22935         * @param {Node} node The node being moved
22936         * @param {Node} oldParent The parent of the node
22937         * @param {Node} newParent The new parent the node is moving to
22938         * @param {Number} index The index it is being moved to
22939         */
22940        "beforemove" : true,
22941        /**
22942         * @event beforeinsert
22943         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22944         * @param {Tree} tree The owner tree
22945         * @param {Node} parent The parent node
22946         * @param {Node} node The child node to be inserted
22947         * @param {Node} refNode The child node the node is being inserted before
22948         */
22949        "beforeinsert" : true
22950    });
22951
22952     Roo.data.Tree.superclass.constructor.call(this);
22953 };
22954
22955 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22956     pathSeparator: "/",
22957
22958     proxyNodeEvent : function(){
22959         return this.fireEvent.apply(this, arguments);
22960     },
22961
22962     /**
22963      * Returns the root node for this tree.
22964      * @return {Node}
22965      */
22966     getRootNode : function(){
22967         return this.root;
22968     },
22969
22970     /**
22971      * Sets the root node for this tree.
22972      * @param {Node} node
22973      * @return {Node}
22974      */
22975     setRootNode : function(node){
22976         this.root = node;
22977         node.ownerTree = this;
22978         node.isRoot = true;
22979         this.registerNode(node);
22980         return node;
22981     },
22982
22983     /**
22984      * Gets a node in this tree by its id.
22985      * @param {String} id
22986      * @return {Node}
22987      */
22988     getNodeById : function(id){
22989         return this.nodeHash[id];
22990     },
22991
22992     registerNode : function(node){
22993         this.nodeHash[node.id] = node;
22994     },
22995
22996     unregisterNode : function(node){
22997         delete this.nodeHash[node.id];
22998     },
22999
23000     toString : function(){
23001         return "[Tree"+(this.id?" "+this.id:"")+"]";
23002     }
23003 });
23004
23005 /**
23006  * @class Roo.data.Node
23007  * @extends Roo.util.Observable
23008  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23009  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23010  * @constructor
23011  * @param {Object} attributes The attributes/config for the node
23012  */
23013 Roo.data.Node = function(attributes){
23014     /**
23015      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23016      * @type {Object}
23017      */
23018     this.attributes = attributes || {};
23019     this.leaf = this.attributes.leaf;
23020     /**
23021      * The node id. @type String
23022      */
23023     this.id = this.attributes.id;
23024     if(!this.id){
23025         this.id = Roo.id(null, "ynode-");
23026         this.attributes.id = this.id;
23027     }
23028      
23029     
23030     /**
23031      * All child nodes of this node. @type Array
23032      */
23033     this.childNodes = [];
23034     if(!this.childNodes.indexOf){ // indexOf is a must
23035         this.childNodes.indexOf = function(o){
23036             for(var i = 0, len = this.length; i < len; i++){
23037                 if(this[i] == o) {
23038                     return i;
23039                 }
23040             }
23041             return -1;
23042         };
23043     }
23044     /**
23045      * The parent node for this node. @type Node
23046      */
23047     this.parentNode = null;
23048     /**
23049      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23050      */
23051     this.firstChild = null;
23052     /**
23053      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23054      */
23055     this.lastChild = null;
23056     /**
23057      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23058      */
23059     this.previousSibling = null;
23060     /**
23061      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23062      */
23063     this.nextSibling = null;
23064
23065     this.addEvents({
23066        /**
23067         * @event append
23068         * Fires when a new child node is appended
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} this This node
23071         * @param {Node} node The newly appended node
23072         * @param {Number} index The index of the newly appended node
23073         */
23074        "append" : true,
23075        /**
23076         * @event remove
23077         * Fires when a child node is removed
23078         * @param {Tree} tree The owner tree
23079         * @param {Node} this This node
23080         * @param {Node} node The removed node
23081         */
23082        "remove" : true,
23083        /**
23084         * @event move
23085         * Fires when this node is moved to a new location in the tree
23086         * @param {Tree} tree The owner tree
23087         * @param {Node} this This node
23088         * @param {Node} oldParent The old parent of this node
23089         * @param {Node} newParent The new parent of this node
23090         * @param {Number} index The index it was moved to
23091         */
23092        "move" : true,
23093        /**
23094         * @event insert
23095         * Fires when a new child node is inserted.
23096         * @param {Tree} tree The owner tree
23097         * @param {Node} this This node
23098         * @param {Node} node The child node inserted
23099         * @param {Node} refNode The child node the node was inserted before
23100         */
23101        "insert" : true,
23102        /**
23103         * @event beforeappend
23104         * Fires before a new child is appended, return false to cancel the append.
23105         * @param {Tree} tree The owner tree
23106         * @param {Node} this This node
23107         * @param {Node} node The child node to be appended
23108         */
23109        "beforeappend" : true,
23110        /**
23111         * @event beforeremove
23112         * Fires before a child is removed, return false to cancel the remove.
23113         * @param {Tree} tree The owner tree
23114         * @param {Node} this This node
23115         * @param {Node} node The child node to be removed
23116         */
23117        "beforeremove" : true,
23118        /**
23119         * @event beforemove
23120         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23121         * @param {Tree} tree The owner tree
23122         * @param {Node} this This node
23123         * @param {Node} oldParent The parent of this node
23124         * @param {Node} newParent The new parent this node is moving to
23125         * @param {Number} index The index it is being moved to
23126         */
23127        "beforemove" : true,
23128        /**
23129         * @event beforeinsert
23130         * Fires before a new child is inserted, return false to cancel the insert.
23131         * @param {Tree} tree The owner tree
23132         * @param {Node} this This node
23133         * @param {Node} node The child node to be inserted
23134         * @param {Node} refNode The child node the node is being inserted before
23135         */
23136        "beforeinsert" : true
23137    });
23138     this.listeners = this.attributes.listeners;
23139     Roo.data.Node.superclass.constructor.call(this);
23140 };
23141
23142 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23143     fireEvent : function(evtName){
23144         // first do standard event for this node
23145         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23146             return false;
23147         }
23148         // then bubble it up to the tree if the event wasn't cancelled
23149         var ot = this.getOwnerTree();
23150         if(ot){
23151             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23152                 return false;
23153             }
23154         }
23155         return true;
23156     },
23157
23158     /**
23159      * Returns true if this node is a leaf
23160      * @return {Boolean}
23161      */
23162     isLeaf : function(){
23163         return this.leaf === true;
23164     },
23165
23166     // private
23167     setFirstChild : function(node){
23168         this.firstChild = node;
23169     },
23170
23171     //private
23172     setLastChild : function(node){
23173         this.lastChild = node;
23174     },
23175
23176
23177     /**
23178      * Returns true if this node is the last child of its parent
23179      * @return {Boolean}
23180      */
23181     isLast : function(){
23182        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23183     },
23184
23185     /**
23186      * Returns true if this node is the first child of its parent
23187      * @return {Boolean}
23188      */
23189     isFirst : function(){
23190        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23191     },
23192
23193     hasChildNodes : function(){
23194         return !this.isLeaf() && this.childNodes.length > 0;
23195     },
23196
23197     /**
23198      * Insert node(s) as the last child node of this node.
23199      * @param {Node/Array} node The node or Array of nodes to append
23200      * @return {Node} The appended node if single append, or null if an array was passed
23201      */
23202     appendChild : function(node){
23203         var multi = false;
23204         if(node instanceof Array){
23205             multi = node;
23206         }else if(arguments.length > 1){
23207             multi = arguments;
23208         }
23209         // if passed an array or multiple args do them one by one
23210         if(multi){
23211             for(var i = 0, len = multi.length; i < len; i++) {
23212                 this.appendChild(multi[i]);
23213             }
23214         }else{
23215             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23216                 return false;
23217             }
23218             var index = this.childNodes.length;
23219             var oldParent = node.parentNode;
23220             // it's a move, make sure we move it cleanly
23221             if(oldParent){
23222                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23223                     return false;
23224                 }
23225                 oldParent.removeChild(node);
23226             }
23227             index = this.childNodes.length;
23228             if(index == 0){
23229                 this.setFirstChild(node);
23230             }
23231             this.childNodes.push(node);
23232             node.parentNode = this;
23233             var ps = this.childNodes[index-1];
23234             if(ps){
23235                 node.previousSibling = ps;
23236                 ps.nextSibling = node;
23237             }else{
23238                 node.previousSibling = null;
23239             }
23240             node.nextSibling = null;
23241             this.setLastChild(node);
23242             node.setOwnerTree(this.getOwnerTree());
23243             this.fireEvent("append", this.ownerTree, this, node, index);
23244             if(oldParent){
23245                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23246             }
23247             return node;
23248         }
23249     },
23250
23251     /**
23252      * Removes a child node from this node.
23253      * @param {Node} node The node to remove
23254      * @return {Node} The removed node
23255      */
23256     removeChild : function(node){
23257         var index = this.childNodes.indexOf(node);
23258         if(index == -1){
23259             return false;
23260         }
23261         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23262             return false;
23263         }
23264
23265         // remove it from childNodes collection
23266         this.childNodes.splice(index, 1);
23267
23268         // update siblings
23269         if(node.previousSibling){
23270             node.previousSibling.nextSibling = node.nextSibling;
23271         }
23272         if(node.nextSibling){
23273             node.nextSibling.previousSibling = node.previousSibling;
23274         }
23275
23276         // update child refs
23277         if(this.firstChild == node){
23278             this.setFirstChild(node.nextSibling);
23279         }
23280         if(this.lastChild == node){
23281             this.setLastChild(node.previousSibling);
23282         }
23283
23284         node.setOwnerTree(null);
23285         // clear any references from the node
23286         node.parentNode = null;
23287         node.previousSibling = null;
23288         node.nextSibling = null;
23289         this.fireEvent("remove", this.ownerTree, this, node);
23290         return node;
23291     },
23292
23293     /**
23294      * Inserts the first node before the second node in this nodes childNodes collection.
23295      * @param {Node} node The node to insert
23296      * @param {Node} refNode The node to insert before (if null the node is appended)
23297      * @return {Node} The inserted node
23298      */
23299     insertBefore : function(node, refNode){
23300         if(!refNode){ // like standard Dom, refNode can be null for append
23301             return this.appendChild(node);
23302         }
23303         // nothing to do
23304         if(node == refNode){
23305             return false;
23306         }
23307
23308         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23309             return false;
23310         }
23311         var index = this.childNodes.indexOf(refNode);
23312         var oldParent = node.parentNode;
23313         var refIndex = index;
23314
23315         // when moving internally, indexes will change after remove
23316         if(oldParent == this && this.childNodes.indexOf(node) < index){
23317             refIndex--;
23318         }
23319
23320         // it's a move, make sure we move it cleanly
23321         if(oldParent){
23322             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23323                 return false;
23324             }
23325             oldParent.removeChild(node);
23326         }
23327         if(refIndex == 0){
23328             this.setFirstChild(node);
23329         }
23330         this.childNodes.splice(refIndex, 0, node);
23331         node.parentNode = this;
23332         var ps = this.childNodes[refIndex-1];
23333         if(ps){
23334             node.previousSibling = ps;
23335             ps.nextSibling = node;
23336         }else{
23337             node.previousSibling = null;
23338         }
23339         node.nextSibling = refNode;
23340         refNode.previousSibling = node;
23341         node.setOwnerTree(this.getOwnerTree());
23342         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23343         if(oldParent){
23344             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23345         }
23346         return node;
23347     },
23348
23349     /**
23350      * Returns the child node at the specified index.
23351      * @param {Number} index
23352      * @return {Node}
23353      */
23354     item : function(index){
23355         return this.childNodes[index];
23356     },
23357
23358     /**
23359      * Replaces one child node in this node with another.
23360      * @param {Node} newChild The replacement node
23361      * @param {Node} oldChild The node to replace
23362      * @return {Node} The replaced node
23363      */
23364     replaceChild : function(newChild, oldChild){
23365         this.insertBefore(newChild, oldChild);
23366         this.removeChild(oldChild);
23367         return oldChild;
23368     },
23369
23370     /**
23371      * Returns the index of a child node
23372      * @param {Node} node
23373      * @return {Number} The index of the node or -1 if it was not found
23374      */
23375     indexOf : function(child){
23376         return this.childNodes.indexOf(child);
23377     },
23378
23379     /**
23380      * Returns the tree this node is in.
23381      * @return {Tree}
23382      */
23383     getOwnerTree : function(){
23384         // if it doesn't have one, look for one
23385         if(!this.ownerTree){
23386             var p = this;
23387             while(p){
23388                 if(p.ownerTree){
23389                     this.ownerTree = p.ownerTree;
23390                     break;
23391                 }
23392                 p = p.parentNode;
23393             }
23394         }
23395         return this.ownerTree;
23396     },
23397
23398     /**
23399      * Returns depth of this node (the root node has a depth of 0)
23400      * @return {Number}
23401      */
23402     getDepth : function(){
23403         var depth = 0;
23404         var p = this;
23405         while(p.parentNode){
23406             ++depth;
23407             p = p.parentNode;
23408         }
23409         return depth;
23410     },
23411
23412     // private
23413     setOwnerTree : function(tree){
23414         // if it's move, we need to update everyone
23415         if(tree != this.ownerTree){
23416             if(this.ownerTree){
23417                 this.ownerTree.unregisterNode(this);
23418             }
23419             this.ownerTree = tree;
23420             var cs = this.childNodes;
23421             for(var i = 0, len = cs.length; i < len; i++) {
23422                 cs[i].setOwnerTree(tree);
23423             }
23424             if(tree){
23425                 tree.registerNode(this);
23426             }
23427         }
23428     },
23429
23430     /**
23431      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23432      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23433      * @return {String} The path
23434      */
23435     getPath : function(attr){
23436         attr = attr || "id";
23437         var p = this.parentNode;
23438         var b = [this.attributes[attr]];
23439         while(p){
23440             b.unshift(p.attributes[attr]);
23441             p = p.parentNode;
23442         }
23443         var sep = this.getOwnerTree().pathSeparator;
23444         return sep + b.join(sep);
23445     },
23446
23447     /**
23448      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23449      * function call will be the scope provided or the current node. The arguments to the function
23450      * will be the args provided or the current node. If the function returns false at any point,
23451      * the bubble is stopped.
23452      * @param {Function} fn The function to call
23453      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23454      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23455      */
23456     bubble : function(fn, scope, args){
23457         var p = this;
23458         while(p){
23459             if(fn.call(scope || p, args || p) === false){
23460                 break;
23461             }
23462             p = p.parentNode;
23463         }
23464     },
23465
23466     /**
23467      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23468      * function call will be the scope provided or the current node. The arguments to the function
23469      * will be the args provided or the current node. If the function returns false at any point,
23470      * the cascade is stopped on that branch.
23471      * @param {Function} fn The function to call
23472      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23473      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23474      */
23475     cascade : function(fn, scope, args){
23476         if(fn.call(scope || this, args || this) !== false){
23477             var cs = this.childNodes;
23478             for(var i = 0, len = cs.length; i < len; i++) {
23479                 cs[i].cascade(fn, scope, args);
23480             }
23481         }
23482     },
23483
23484     /**
23485      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23486      * function call will be the scope provided or the current node. The arguments to the function
23487      * will be the args provided or the current node. If the function returns false at any point,
23488      * the iteration stops.
23489      * @param {Function} fn The function to call
23490      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23491      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23492      */
23493     eachChild : function(fn, scope, args){
23494         var cs = this.childNodes;
23495         for(var i = 0, len = cs.length; i < len; i++) {
23496                 if(fn.call(scope || this, args || cs[i]) === false){
23497                     break;
23498                 }
23499         }
23500     },
23501
23502     /**
23503      * Finds the first child that has the attribute with the specified value.
23504      * @param {String} attribute The attribute name
23505      * @param {Mixed} value The value to search for
23506      * @return {Node} The found child or null if none was found
23507      */
23508     findChild : function(attribute, value){
23509         var cs = this.childNodes;
23510         for(var i = 0, len = cs.length; i < len; i++) {
23511                 if(cs[i].attributes[attribute] == value){
23512                     return cs[i];
23513                 }
23514         }
23515         return null;
23516     },
23517
23518     /**
23519      * Finds the first child by a custom function. The child matches if the function passed
23520      * returns true.
23521      * @param {Function} fn
23522      * @param {Object} scope (optional)
23523      * @return {Node} The found child or null if none was found
23524      */
23525     findChildBy : function(fn, scope){
23526         var cs = this.childNodes;
23527         for(var i = 0, len = cs.length; i < len; i++) {
23528                 if(fn.call(scope||cs[i], cs[i]) === true){
23529                     return cs[i];
23530                 }
23531         }
23532         return null;
23533     },
23534
23535     /**
23536      * Sorts this nodes children using the supplied sort function
23537      * @param {Function} fn
23538      * @param {Object} scope (optional)
23539      */
23540     sort : function(fn, scope){
23541         var cs = this.childNodes;
23542         var len = cs.length;
23543         if(len > 0){
23544             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23545             cs.sort(sortFn);
23546             for(var i = 0; i < len; i++){
23547                 var n = cs[i];
23548                 n.previousSibling = cs[i-1];
23549                 n.nextSibling = cs[i+1];
23550                 if(i == 0){
23551                     this.setFirstChild(n);
23552                 }
23553                 if(i == len-1){
23554                     this.setLastChild(n);
23555                 }
23556             }
23557         }
23558     },
23559
23560     /**
23561      * Returns true if this node is an ancestor (at any point) of the passed node.
23562      * @param {Node} node
23563      * @return {Boolean}
23564      */
23565     contains : function(node){
23566         return node.isAncestor(this);
23567     },
23568
23569     /**
23570      * Returns true if the passed node is an ancestor (at any point) of this node.
23571      * @param {Node} node
23572      * @return {Boolean}
23573      */
23574     isAncestor : function(node){
23575         var p = this.parentNode;
23576         while(p){
23577             if(p == node){
23578                 return true;
23579             }
23580             p = p.parentNode;
23581         }
23582         return false;
23583     },
23584
23585     toString : function(){
23586         return "[Node"+(this.id?" "+this.id:"")+"]";
23587     }
23588 });/*
23589  * Based on:
23590  * Ext JS Library 1.1.1
23591  * Copyright(c) 2006-2007, Ext JS, LLC.
23592  *
23593  * Originally Released Under LGPL - original licence link has changed is not relivant.
23594  *
23595  * Fork - LGPL
23596  * <script type="text/javascript">
23597  */
23598  (function(){ 
23599 /**
23600  * @class Roo.Layer
23601  * @extends Roo.Element
23602  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23603  * automatic maintaining of shadow/shim positions.
23604  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23605  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23606  * you can pass a string with a CSS class name. False turns off the shadow.
23607  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23608  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23609  * @cfg {String} cls CSS class to add to the element
23610  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23611  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23612  * @constructor
23613  * @param {Object} config An object with config options.
23614  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23615  */
23616
23617 Roo.Layer = function(config, existingEl){
23618     config = config || {};
23619     var dh = Roo.DomHelper;
23620     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23621     if(existingEl){
23622         this.dom = Roo.getDom(existingEl);
23623     }
23624     if(!this.dom){
23625         var o = config.dh || {tag: "div", cls: "x-layer"};
23626         this.dom = dh.append(pel, o);
23627     }
23628     if(config.cls){
23629         this.addClass(config.cls);
23630     }
23631     this.constrain = config.constrain !== false;
23632     this.visibilityMode = Roo.Element.VISIBILITY;
23633     if(config.id){
23634         this.id = this.dom.id = config.id;
23635     }else{
23636         this.id = Roo.id(this.dom);
23637     }
23638     this.zindex = config.zindex || this.getZIndex();
23639     this.position("absolute", this.zindex);
23640     if(config.shadow){
23641         this.shadowOffset = config.shadowOffset || 4;
23642         this.shadow = new Roo.Shadow({
23643             offset : this.shadowOffset,
23644             mode : config.shadow
23645         });
23646     }else{
23647         this.shadowOffset = 0;
23648     }
23649     this.useShim = config.shim !== false && Roo.useShims;
23650     this.useDisplay = config.useDisplay;
23651     this.hide();
23652 };
23653
23654 var supr = Roo.Element.prototype;
23655
23656 // shims are shared among layer to keep from having 100 iframes
23657 var shims = [];
23658
23659 Roo.extend(Roo.Layer, Roo.Element, {
23660
23661     getZIndex : function(){
23662         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23663     },
23664
23665     getShim : function(){
23666         if(!this.useShim){
23667             return null;
23668         }
23669         if(this.shim){
23670             return this.shim;
23671         }
23672         var shim = shims.shift();
23673         if(!shim){
23674             shim = this.createShim();
23675             shim.enableDisplayMode('block');
23676             shim.dom.style.display = 'none';
23677             shim.dom.style.visibility = 'visible';
23678         }
23679         var pn = this.dom.parentNode;
23680         if(shim.dom.parentNode != pn){
23681             pn.insertBefore(shim.dom, this.dom);
23682         }
23683         shim.setStyle('z-index', this.getZIndex()-2);
23684         this.shim = shim;
23685         return shim;
23686     },
23687
23688     hideShim : function(){
23689         if(this.shim){
23690             this.shim.setDisplayed(false);
23691             shims.push(this.shim);
23692             delete this.shim;
23693         }
23694     },
23695
23696     disableShadow : function(){
23697         if(this.shadow){
23698             this.shadowDisabled = true;
23699             this.shadow.hide();
23700             this.lastShadowOffset = this.shadowOffset;
23701             this.shadowOffset = 0;
23702         }
23703     },
23704
23705     enableShadow : function(show){
23706         if(this.shadow){
23707             this.shadowDisabled = false;
23708             this.shadowOffset = this.lastShadowOffset;
23709             delete this.lastShadowOffset;
23710             if(show){
23711                 this.sync(true);
23712             }
23713         }
23714     },
23715
23716     // private
23717     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23718     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23719     sync : function(doShow){
23720         var sw = this.shadow;
23721         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23722             var sh = this.getShim();
23723
23724             var w = this.getWidth(),
23725                 h = this.getHeight();
23726
23727             var l = this.getLeft(true),
23728                 t = this.getTop(true);
23729
23730             if(sw && !this.shadowDisabled){
23731                 if(doShow && !sw.isVisible()){
23732                     sw.show(this);
23733                 }else{
23734                     sw.realign(l, t, w, h);
23735                 }
23736                 if(sh){
23737                     if(doShow){
23738                        sh.show();
23739                     }
23740                     // fit the shim behind the shadow, so it is shimmed too
23741                     var a = sw.adjusts, s = sh.dom.style;
23742                     s.left = (Math.min(l, l+a.l))+"px";
23743                     s.top = (Math.min(t, t+a.t))+"px";
23744                     s.width = (w+a.w)+"px";
23745                     s.height = (h+a.h)+"px";
23746                 }
23747             }else if(sh){
23748                 if(doShow){
23749                    sh.show();
23750                 }
23751                 sh.setSize(w, h);
23752                 sh.setLeftTop(l, t);
23753             }
23754             
23755         }
23756     },
23757
23758     // private
23759     destroy : function(){
23760         this.hideShim();
23761         if(this.shadow){
23762             this.shadow.hide();
23763         }
23764         this.removeAllListeners();
23765         var pn = this.dom.parentNode;
23766         if(pn){
23767             pn.removeChild(this.dom);
23768         }
23769         Roo.Element.uncache(this.id);
23770     },
23771
23772     remove : function(){
23773         this.destroy();
23774     },
23775
23776     // private
23777     beginUpdate : function(){
23778         this.updating = true;
23779     },
23780
23781     // private
23782     endUpdate : function(){
23783         this.updating = false;
23784         this.sync(true);
23785     },
23786
23787     // private
23788     hideUnders : function(negOffset){
23789         if(this.shadow){
23790             this.shadow.hide();
23791         }
23792         this.hideShim();
23793     },
23794
23795     // private
23796     constrainXY : function(){
23797         if(this.constrain){
23798             var vw = Roo.lib.Dom.getViewWidth(),
23799                 vh = Roo.lib.Dom.getViewHeight();
23800             var s = Roo.get(document).getScroll();
23801
23802             var xy = this.getXY();
23803             var x = xy[0], y = xy[1];   
23804             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23805             // only move it if it needs it
23806             var moved = false;
23807             // first validate right/bottom
23808             if((x + w) > vw+s.left){
23809                 x = vw - w - this.shadowOffset;
23810                 moved = true;
23811             }
23812             if((y + h) > vh+s.top){
23813                 y = vh - h - this.shadowOffset;
23814                 moved = true;
23815             }
23816             // then make sure top/left isn't negative
23817             if(x < s.left){
23818                 x = s.left;
23819                 moved = true;
23820             }
23821             if(y < s.top){
23822                 y = s.top;
23823                 moved = true;
23824             }
23825             if(moved){
23826                 if(this.avoidY){
23827                     var ay = this.avoidY;
23828                     if(y <= ay && (y+h) >= ay){
23829                         y = ay-h-5;   
23830                     }
23831                 }
23832                 xy = [x, y];
23833                 this.storeXY(xy);
23834                 supr.setXY.call(this, xy);
23835                 this.sync();
23836             }
23837         }
23838     },
23839
23840     isVisible : function(){
23841         return this.visible;    
23842     },
23843
23844     // private
23845     showAction : function(){
23846         this.visible = true; // track visibility to prevent getStyle calls
23847         if(this.useDisplay === true){
23848             this.setDisplayed("");
23849         }else if(this.lastXY){
23850             supr.setXY.call(this, this.lastXY);
23851         }else if(this.lastLT){
23852             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23853         }
23854     },
23855
23856     // private
23857     hideAction : function(){
23858         this.visible = false;
23859         if(this.useDisplay === true){
23860             this.setDisplayed(false);
23861         }else{
23862             this.setLeftTop(-10000,-10000);
23863         }
23864     },
23865
23866     // overridden Element method
23867     setVisible : function(v, a, d, c, e){
23868         if(v){
23869             this.showAction();
23870         }
23871         if(a && v){
23872             var cb = function(){
23873                 this.sync(true);
23874                 if(c){
23875                     c();
23876                 }
23877             }.createDelegate(this);
23878             supr.setVisible.call(this, true, true, d, cb, e);
23879         }else{
23880             if(!v){
23881                 this.hideUnders(true);
23882             }
23883             var cb = c;
23884             if(a){
23885                 cb = function(){
23886                     this.hideAction();
23887                     if(c){
23888                         c();
23889                     }
23890                 }.createDelegate(this);
23891             }
23892             supr.setVisible.call(this, v, a, d, cb, e);
23893             if(v){
23894                 this.sync(true);
23895             }else if(!a){
23896                 this.hideAction();
23897             }
23898         }
23899     },
23900
23901     storeXY : function(xy){
23902         delete this.lastLT;
23903         this.lastXY = xy;
23904     },
23905
23906     storeLeftTop : function(left, top){
23907         delete this.lastXY;
23908         this.lastLT = [left, top];
23909     },
23910
23911     // private
23912     beforeFx : function(){
23913         this.beforeAction();
23914         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23915     },
23916
23917     // private
23918     afterFx : function(){
23919         Roo.Layer.superclass.afterFx.apply(this, arguments);
23920         this.sync(this.isVisible());
23921     },
23922
23923     // private
23924     beforeAction : function(){
23925         if(!this.updating && this.shadow){
23926             this.shadow.hide();
23927         }
23928     },
23929
23930     // overridden Element method
23931     setLeft : function(left){
23932         this.storeLeftTop(left, this.getTop(true));
23933         supr.setLeft.apply(this, arguments);
23934         this.sync();
23935     },
23936
23937     setTop : function(top){
23938         this.storeLeftTop(this.getLeft(true), top);
23939         supr.setTop.apply(this, arguments);
23940         this.sync();
23941     },
23942
23943     setLeftTop : function(left, top){
23944         this.storeLeftTop(left, top);
23945         supr.setLeftTop.apply(this, arguments);
23946         this.sync();
23947     },
23948
23949     setXY : function(xy, a, d, c, e){
23950         this.fixDisplay();
23951         this.beforeAction();
23952         this.storeXY(xy);
23953         var cb = this.createCB(c);
23954         supr.setXY.call(this, xy, a, d, cb, e);
23955         if(!a){
23956             cb();
23957         }
23958     },
23959
23960     // private
23961     createCB : function(c){
23962         var el = this;
23963         return function(){
23964             el.constrainXY();
23965             el.sync(true);
23966             if(c){
23967                 c();
23968             }
23969         };
23970     },
23971
23972     // overridden Element method
23973     setX : function(x, a, d, c, e){
23974         this.setXY([x, this.getY()], a, d, c, e);
23975     },
23976
23977     // overridden Element method
23978     setY : function(y, a, d, c, e){
23979         this.setXY([this.getX(), y], a, d, c, e);
23980     },
23981
23982     // overridden Element method
23983     setSize : function(w, h, a, d, c, e){
23984         this.beforeAction();
23985         var cb = this.createCB(c);
23986         supr.setSize.call(this, w, h, a, d, cb, e);
23987         if(!a){
23988             cb();
23989         }
23990     },
23991
23992     // overridden Element method
23993     setWidth : function(w, a, d, c, e){
23994         this.beforeAction();
23995         var cb = this.createCB(c);
23996         supr.setWidth.call(this, w, a, d, cb, e);
23997         if(!a){
23998             cb();
23999         }
24000     },
24001
24002     // overridden Element method
24003     setHeight : function(h, a, d, c, e){
24004         this.beforeAction();
24005         var cb = this.createCB(c);
24006         supr.setHeight.call(this, h, a, d, cb, e);
24007         if(!a){
24008             cb();
24009         }
24010     },
24011
24012     // overridden Element method
24013     setBounds : function(x, y, w, h, a, d, c, e){
24014         this.beforeAction();
24015         var cb = this.createCB(c);
24016         if(!a){
24017             this.storeXY([x, y]);
24018             supr.setXY.call(this, [x, y]);
24019             supr.setSize.call(this, w, h, a, d, cb, e);
24020             cb();
24021         }else{
24022             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24023         }
24024         return this;
24025     },
24026     
24027     /**
24028      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24029      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24030      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24031      * @param {Number} zindex The new z-index to set
24032      * @return {this} The Layer
24033      */
24034     setZIndex : function(zindex){
24035         this.zindex = zindex;
24036         this.setStyle("z-index", zindex + 2);
24037         if(this.shadow){
24038             this.shadow.setZIndex(zindex + 1);
24039         }
24040         if(this.shim){
24041             this.shim.setStyle("z-index", zindex);
24042         }
24043     }
24044 });
24045 })();/*
24046  * Based on:
24047  * Ext JS Library 1.1.1
24048  * Copyright(c) 2006-2007, Ext JS, LLC.
24049  *
24050  * Originally Released Under LGPL - original licence link has changed is not relivant.
24051  *
24052  * Fork - LGPL
24053  * <script type="text/javascript">
24054  */
24055
24056
24057 /**
24058  * @class Roo.Shadow
24059  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24060  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24061  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24062  * @constructor
24063  * Create a new Shadow
24064  * @param {Object} config The config object
24065  */
24066 Roo.Shadow = function(config){
24067     Roo.apply(this, config);
24068     if(typeof this.mode != "string"){
24069         this.mode = this.defaultMode;
24070     }
24071     var o = this.offset, a = {h: 0};
24072     var rad = Math.floor(this.offset/2);
24073     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24074         case "drop":
24075             a.w = 0;
24076             a.l = a.t = o;
24077             a.t -= 1;
24078             if(Roo.isIE){
24079                 a.l -= this.offset + rad;
24080                 a.t -= this.offset + rad;
24081                 a.w -= rad;
24082                 a.h -= rad;
24083                 a.t += 1;
24084             }
24085         break;
24086         case "sides":
24087             a.w = (o*2);
24088             a.l = -o;
24089             a.t = o-1;
24090             if(Roo.isIE){
24091                 a.l -= (this.offset - rad);
24092                 a.t -= this.offset + rad;
24093                 a.l += 1;
24094                 a.w -= (this.offset - rad)*2;
24095                 a.w -= rad + 1;
24096                 a.h -= 1;
24097             }
24098         break;
24099         case "frame":
24100             a.w = a.h = (o*2);
24101             a.l = a.t = -o;
24102             a.t += 1;
24103             a.h -= 2;
24104             if(Roo.isIE){
24105                 a.l -= (this.offset - rad);
24106                 a.t -= (this.offset - rad);
24107                 a.l += 1;
24108                 a.w -= (this.offset + rad + 1);
24109                 a.h -= (this.offset + rad);
24110                 a.h += 1;
24111             }
24112         break;
24113     };
24114
24115     this.adjusts = a;
24116 };
24117
24118 Roo.Shadow.prototype = {
24119     /**
24120      * @cfg {String} mode
24121      * The shadow display mode.  Supports the following options:<br />
24122      * sides: Shadow displays on both sides and bottom only<br />
24123      * frame: Shadow displays equally on all four sides<br />
24124      * drop: Traditional bottom-right drop shadow (default)
24125      */
24126     /**
24127      * @cfg {String} offset
24128      * The number of pixels to offset the shadow from the element (defaults to 4)
24129      */
24130     offset: 4,
24131
24132     // private
24133     defaultMode: "drop",
24134
24135     /**
24136      * Displays the shadow under the target element
24137      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24138      */
24139     show : function(target){
24140         target = Roo.get(target);
24141         if(!this.el){
24142             this.el = Roo.Shadow.Pool.pull();
24143             if(this.el.dom.nextSibling != target.dom){
24144                 this.el.insertBefore(target);
24145             }
24146         }
24147         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24148         if(Roo.isIE){
24149             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24150         }
24151         this.realign(
24152             target.getLeft(true),
24153             target.getTop(true),
24154             target.getWidth(),
24155             target.getHeight()
24156         );
24157         this.el.dom.style.display = "block";
24158     },
24159
24160     /**
24161      * Returns true if the shadow is visible, else false
24162      */
24163     isVisible : function(){
24164         return this.el ? true : false;  
24165     },
24166
24167     /**
24168      * Direct alignment when values are already available. Show must be called at least once before
24169      * calling this method to ensure it is initialized.
24170      * @param {Number} left The target element left position
24171      * @param {Number} top The target element top position
24172      * @param {Number} width The target element width
24173      * @param {Number} height The target element height
24174      */
24175     realign : function(l, t, w, h){
24176         if(!this.el){
24177             return;
24178         }
24179         var a = this.adjusts, d = this.el.dom, s = d.style;
24180         var iea = 0;
24181         s.left = (l+a.l)+"px";
24182         s.top = (t+a.t)+"px";
24183         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24184  
24185         if(s.width != sws || s.height != shs){
24186             s.width = sws;
24187             s.height = shs;
24188             if(!Roo.isIE){
24189                 var cn = d.childNodes;
24190                 var sww = Math.max(0, (sw-12))+"px";
24191                 cn[0].childNodes[1].style.width = sww;
24192                 cn[1].childNodes[1].style.width = sww;
24193                 cn[2].childNodes[1].style.width = sww;
24194                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24195             }
24196         }
24197     },
24198
24199     /**
24200      * Hides this shadow
24201      */
24202     hide : function(){
24203         if(this.el){
24204             this.el.dom.style.display = "none";
24205             Roo.Shadow.Pool.push(this.el);
24206             delete this.el;
24207         }
24208     },
24209
24210     /**
24211      * Adjust the z-index of this shadow
24212      * @param {Number} zindex The new z-index
24213      */
24214     setZIndex : function(z){
24215         this.zIndex = z;
24216         if(this.el){
24217             this.el.setStyle("z-index", z);
24218         }
24219     }
24220 };
24221
24222 // Private utility class that manages the internal Shadow cache
24223 Roo.Shadow.Pool = function(){
24224     var p = [];
24225     var markup = Roo.isIE ?
24226                  '<div class="x-ie-shadow"></div>' :
24227                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
24228     return {
24229         pull : function(){
24230             var sh = p.shift();
24231             if(!sh){
24232                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24233                 sh.autoBoxAdjust = false;
24234             }
24235             return sh;
24236         },
24237
24238         push : function(sh){
24239             p.push(sh);
24240         }
24241     };
24242 }();/*
24243  * Based on:
24244  * Ext JS Library 1.1.1
24245  * Copyright(c) 2006-2007, Ext JS, LLC.
24246  *
24247  * Originally Released Under LGPL - original licence link has changed is not relivant.
24248  *
24249  * Fork - LGPL
24250  * <script type="text/javascript">
24251  */
24252
24253
24254 /**
24255  * @class Roo.SplitBar
24256  * @extends Roo.util.Observable
24257  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24258  * <br><br>
24259  * Usage:
24260  * <pre><code>
24261 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24262                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24263 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24264 split.minSize = 100;
24265 split.maxSize = 600;
24266 split.animate = true;
24267 split.on('moved', splitterMoved);
24268 </code></pre>
24269  * @constructor
24270  * Create a new SplitBar
24271  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24272  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24273  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24274  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24275                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24276                         position of the SplitBar).
24277  */
24278 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24279     
24280     /** @private */
24281     this.el = Roo.get(dragElement, true);
24282     this.el.dom.unselectable = "on";
24283     /** @private */
24284     this.resizingEl = Roo.get(resizingElement, true);
24285
24286     /**
24287      * @private
24288      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24289      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24290      * @type Number
24291      */
24292     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24293     
24294     /**
24295      * The minimum size of the resizing element. (Defaults to 0)
24296      * @type Number
24297      */
24298     this.minSize = 0;
24299     
24300     /**
24301      * The maximum size of the resizing element. (Defaults to 2000)
24302      * @type Number
24303      */
24304     this.maxSize = 2000;
24305     
24306     /**
24307      * Whether to animate the transition to the new size
24308      * @type Boolean
24309      */
24310     this.animate = false;
24311     
24312     /**
24313      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24314      * @type Boolean
24315      */
24316     this.useShim = false;
24317     
24318     /** @private */
24319     this.shim = null;
24320     
24321     if(!existingProxy){
24322         /** @private */
24323         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24324     }else{
24325         this.proxy = Roo.get(existingProxy).dom;
24326     }
24327     /** @private */
24328     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24329     
24330     /** @private */
24331     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24332     
24333     /** @private */
24334     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24335     
24336     /** @private */
24337     this.dragSpecs = {};
24338     
24339     /**
24340      * @private The adapter to use to positon and resize elements
24341      */
24342     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24343     this.adapter.init(this);
24344     
24345     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24346         /** @private */
24347         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24348         this.el.addClass("x-splitbar-h");
24349     }else{
24350         /** @private */
24351         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24352         this.el.addClass("x-splitbar-v");
24353     }
24354     
24355     this.addEvents({
24356         /**
24357          * @event resize
24358          * Fires when the splitter is moved (alias for {@link #event-moved})
24359          * @param {Roo.SplitBar} this
24360          * @param {Number} newSize the new width or height
24361          */
24362         "resize" : true,
24363         /**
24364          * @event moved
24365          * Fires when the splitter is moved
24366          * @param {Roo.SplitBar} this
24367          * @param {Number} newSize the new width or height
24368          */
24369         "moved" : true,
24370         /**
24371          * @event beforeresize
24372          * Fires before the splitter is dragged
24373          * @param {Roo.SplitBar} this
24374          */
24375         "beforeresize" : true,
24376
24377         "beforeapply" : true
24378     });
24379
24380     Roo.util.Observable.call(this);
24381 };
24382
24383 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24384     onStartProxyDrag : function(x, y){
24385         this.fireEvent("beforeresize", this);
24386         if(!this.overlay){
24387             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24388             o.unselectable();
24389             o.enableDisplayMode("block");
24390             // all splitbars share the same overlay
24391             Roo.SplitBar.prototype.overlay = o;
24392         }
24393         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24394         this.overlay.show();
24395         Roo.get(this.proxy).setDisplayed("block");
24396         var size = this.adapter.getElementSize(this);
24397         this.activeMinSize = this.getMinimumSize();;
24398         this.activeMaxSize = this.getMaximumSize();;
24399         var c1 = size - this.activeMinSize;
24400         var c2 = Math.max(this.activeMaxSize - size, 0);
24401         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24402             this.dd.resetConstraints();
24403             this.dd.setXConstraint(
24404                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24405                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24406             );
24407             this.dd.setYConstraint(0, 0);
24408         }else{
24409             this.dd.resetConstraints();
24410             this.dd.setXConstraint(0, 0);
24411             this.dd.setYConstraint(
24412                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24413                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24414             );
24415          }
24416         this.dragSpecs.startSize = size;
24417         this.dragSpecs.startPoint = [x, y];
24418         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24419     },
24420     
24421     /** 
24422      * @private Called after the drag operation by the DDProxy
24423      */
24424     onEndProxyDrag : function(e){
24425         Roo.get(this.proxy).setDisplayed(false);
24426         var endPoint = Roo.lib.Event.getXY(e);
24427         if(this.overlay){
24428             this.overlay.hide();
24429         }
24430         var newSize;
24431         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24432             newSize = this.dragSpecs.startSize + 
24433                 (this.placement == Roo.SplitBar.LEFT ?
24434                     endPoint[0] - this.dragSpecs.startPoint[0] :
24435                     this.dragSpecs.startPoint[0] - endPoint[0]
24436                 );
24437         }else{
24438             newSize = this.dragSpecs.startSize + 
24439                 (this.placement == Roo.SplitBar.TOP ?
24440                     endPoint[1] - this.dragSpecs.startPoint[1] :
24441                     this.dragSpecs.startPoint[1] - endPoint[1]
24442                 );
24443         }
24444         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24445         if(newSize != this.dragSpecs.startSize){
24446             if(this.fireEvent('beforeapply', this, newSize) !== false){
24447                 this.adapter.setElementSize(this, newSize);
24448                 this.fireEvent("moved", this, newSize);
24449                 this.fireEvent("resize", this, newSize);
24450             }
24451         }
24452     },
24453     
24454     /**
24455      * Get the adapter this SplitBar uses
24456      * @return The adapter object
24457      */
24458     getAdapter : function(){
24459         return this.adapter;
24460     },
24461     
24462     /**
24463      * Set the adapter this SplitBar uses
24464      * @param {Object} adapter A SplitBar adapter object
24465      */
24466     setAdapter : function(adapter){
24467         this.adapter = adapter;
24468         this.adapter.init(this);
24469     },
24470     
24471     /**
24472      * Gets the minimum size for the resizing element
24473      * @return {Number} The minimum size
24474      */
24475     getMinimumSize : function(){
24476         return this.minSize;
24477     },
24478     
24479     /**
24480      * Sets the minimum size for the resizing element
24481      * @param {Number} minSize The minimum size
24482      */
24483     setMinimumSize : function(minSize){
24484         this.minSize = minSize;
24485     },
24486     
24487     /**
24488      * Gets the maximum size for the resizing element
24489      * @return {Number} The maximum size
24490      */
24491     getMaximumSize : function(){
24492         return this.maxSize;
24493     },
24494     
24495     /**
24496      * Sets the maximum size for the resizing element
24497      * @param {Number} maxSize The maximum size
24498      */
24499     setMaximumSize : function(maxSize){
24500         this.maxSize = maxSize;
24501     },
24502     
24503     /**
24504      * Sets the initialize size for the resizing element
24505      * @param {Number} size The initial size
24506      */
24507     setCurrentSize : function(size){
24508         var oldAnimate = this.animate;
24509         this.animate = false;
24510         this.adapter.setElementSize(this, size);
24511         this.animate = oldAnimate;
24512     },
24513     
24514     /**
24515      * Destroy this splitbar. 
24516      * @param {Boolean} removeEl True to remove the element
24517      */
24518     destroy : function(removeEl){
24519         if(this.shim){
24520             this.shim.remove();
24521         }
24522         this.dd.unreg();
24523         this.proxy.parentNode.removeChild(this.proxy);
24524         if(removeEl){
24525             this.el.remove();
24526         }
24527     }
24528 });
24529
24530 /**
24531  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
24532  */
24533 Roo.SplitBar.createProxy = function(dir){
24534     var proxy = new Roo.Element(document.createElement("div"));
24535     proxy.unselectable();
24536     var cls = 'x-splitbar-proxy';
24537     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24538     document.body.appendChild(proxy.dom);
24539     return proxy.dom;
24540 };
24541
24542 /** 
24543  * @class Roo.SplitBar.BasicLayoutAdapter
24544  * Default Adapter. It assumes the splitter and resizing element are not positioned
24545  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24546  */
24547 Roo.SplitBar.BasicLayoutAdapter = function(){
24548 };
24549
24550 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24551     // do nothing for now
24552     init : function(s){
24553     
24554     },
24555     /**
24556      * Called before drag operations to get the current size of the resizing element. 
24557      * @param {Roo.SplitBar} s The SplitBar using this adapter
24558      */
24559      getElementSize : function(s){
24560         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24561             return s.resizingEl.getWidth();
24562         }else{
24563             return s.resizingEl.getHeight();
24564         }
24565     },
24566     
24567     /**
24568      * Called after drag operations to set the size of the resizing element.
24569      * @param {Roo.SplitBar} s The SplitBar using this adapter
24570      * @param {Number} newSize The new size to set
24571      * @param {Function} onComplete A function to be invoked when resizing is complete
24572      */
24573     setElementSize : function(s, newSize, onComplete){
24574         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24575             if(!s.animate){
24576                 s.resizingEl.setWidth(newSize);
24577                 if(onComplete){
24578                     onComplete(s, newSize);
24579                 }
24580             }else{
24581                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24582             }
24583         }else{
24584             
24585             if(!s.animate){
24586                 s.resizingEl.setHeight(newSize);
24587                 if(onComplete){
24588                     onComplete(s, newSize);
24589                 }
24590             }else{
24591                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24592             }
24593         }
24594     }
24595 };
24596
24597 /** 
24598  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24599  * @extends Roo.SplitBar.BasicLayoutAdapter
24600  * Adapter that  moves the splitter element to align with the resized sizing element. 
24601  * Used with an absolute positioned SplitBar.
24602  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24603  * document.body, make sure you assign an id to the body element.
24604  */
24605 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24606     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24607     this.container = Roo.get(container);
24608 };
24609
24610 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24611     init : function(s){
24612         this.basic.init(s);
24613     },
24614     
24615     getElementSize : function(s){
24616         return this.basic.getElementSize(s);
24617     },
24618     
24619     setElementSize : function(s, newSize, onComplete){
24620         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24621     },
24622     
24623     moveSplitter : function(s){
24624         var yes = Roo.SplitBar;
24625         switch(s.placement){
24626             case yes.LEFT:
24627                 s.el.setX(s.resizingEl.getRight());
24628                 break;
24629             case yes.RIGHT:
24630                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24631                 break;
24632             case yes.TOP:
24633                 s.el.setY(s.resizingEl.getBottom());
24634                 break;
24635             case yes.BOTTOM:
24636                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24637                 break;
24638         }
24639     }
24640 };
24641
24642 /**
24643  * Orientation constant - Create a vertical SplitBar
24644  * @static
24645  * @type Number
24646  */
24647 Roo.SplitBar.VERTICAL = 1;
24648
24649 /**
24650  * Orientation constant - Create a horizontal SplitBar
24651  * @static
24652  * @type Number
24653  */
24654 Roo.SplitBar.HORIZONTAL = 2;
24655
24656 /**
24657  * Placement constant - The resizing element is to the left of the splitter element
24658  * @static
24659  * @type Number
24660  */
24661 Roo.SplitBar.LEFT = 1;
24662
24663 /**
24664  * Placement constant - The resizing element is to the right of the splitter element
24665  * @static
24666  * @type Number
24667  */
24668 Roo.SplitBar.RIGHT = 2;
24669
24670 /**
24671  * Placement constant - The resizing element is positioned above the splitter element
24672  * @static
24673  * @type Number
24674  */
24675 Roo.SplitBar.TOP = 3;
24676
24677 /**
24678  * Placement constant - The resizing element is positioned under splitter element
24679  * @static
24680  * @type Number
24681  */
24682 Roo.SplitBar.BOTTOM = 4;
24683 /*
24684  * Based on:
24685  * Ext JS Library 1.1.1
24686  * Copyright(c) 2006-2007, Ext JS, LLC.
24687  *
24688  * Originally Released Under LGPL - original licence link has changed is not relivant.
24689  *
24690  * Fork - LGPL
24691  * <script type="text/javascript">
24692  */
24693
24694 /**
24695  * @class Roo.View
24696  * @extends Roo.util.Observable
24697  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24698  * This class also supports single and multi selection modes. <br>
24699  * Create a data model bound view:
24700  <pre><code>
24701  var store = new Roo.data.Store(...);
24702
24703  var view = new Roo.View({
24704     el : "my-element",
24705     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24706  
24707     singleSelect: true,
24708     selectedClass: "ydataview-selected",
24709     store: store
24710  });
24711
24712  // listen for node click?
24713  view.on("click", function(vw, index, node, e){
24714  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24715  });
24716
24717  // load XML data
24718  dataModel.load("foobar.xml");
24719  </code></pre>
24720  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24721  * <br><br>
24722  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24723  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24724  * 
24725  * Note: old style constructor is still suported (container, template, config)
24726  * 
24727  * @constructor
24728  * Create a new View
24729  * @param {Object} config The config object
24730  * 
24731  */
24732 Roo.View = function(config, depreciated_tpl, depreciated_config){
24733     
24734     if (typeof(depreciated_tpl) == 'undefined') {
24735         // new way.. - universal constructor.
24736         Roo.apply(this, config);
24737         this.el  = Roo.get(this.el);
24738     } else {
24739         // old format..
24740         this.el  = Roo.get(config);
24741         this.tpl = depreciated_tpl;
24742         Roo.apply(this, depreciated_config);
24743     }
24744     this.wrapEl  = this.el.wrap().wrap();
24745     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24746     
24747     
24748     if(typeof(this.tpl) == "string"){
24749         this.tpl = new Roo.Template(this.tpl);
24750     } else {
24751         // support xtype ctors..
24752         this.tpl = new Roo.factory(this.tpl, Roo);
24753     }
24754     
24755     
24756     this.tpl.compile();
24757    
24758   
24759     
24760      
24761     /** @private */
24762     this.addEvents({
24763         /**
24764          * @event beforeclick
24765          * Fires before a click is processed. Returns false to cancel the default action.
24766          * @param {Roo.View} this
24767          * @param {Number} index The index of the target node
24768          * @param {HTMLElement} node The target node
24769          * @param {Roo.EventObject} e The raw event object
24770          */
24771             "beforeclick" : true,
24772         /**
24773          * @event click
24774          * Fires when a template node is clicked.
24775          * @param {Roo.View} this
24776          * @param {Number} index The index of the target node
24777          * @param {HTMLElement} node The target node
24778          * @param {Roo.EventObject} e The raw event object
24779          */
24780             "click" : true,
24781         /**
24782          * @event dblclick
24783          * Fires when a template node is double clicked.
24784          * @param {Roo.View} this
24785          * @param {Number} index The index of the target node
24786          * @param {HTMLElement} node The target node
24787          * @param {Roo.EventObject} e The raw event object
24788          */
24789             "dblclick" : true,
24790         /**
24791          * @event contextmenu
24792          * Fires when a template node is right clicked.
24793          * @param {Roo.View} this
24794          * @param {Number} index The index of the target node
24795          * @param {HTMLElement} node The target node
24796          * @param {Roo.EventObject} e The raw event object
24797          */
24798             "contextmenu" : true,
24799         /**
24800          * @event selectionchange
24801          * Fires when the selected nodes change.
24802          * @param {Roo.View} this
24803          * @param {Array} selections Array of the selected nodes
24804          */
24805             "selectionchange" : true,
24806     
24807         /**
24808          * @event beforeselect
24809          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24810          * @param {Roo.View} this
24811          * @param {HTMLElement} node The node to be selected
24812          * @param {Array} selections Array of currently selected nodes
24813          */
24814             "beforeselect" : true,
24815         /**
24816          * @event preparedata
24817          * Fires on every row to render, to allow you to change the data.
24818          * @param {Roo.View} this
24819          * @param {Object} data to be rendered (change this)
24820          */
24821           "preparedata" : true
24822           
24823           
24824         });
24825
24826
24827
24828     this.el.on({
24829         "click": this.onClick,
24830         "dblclick": this.onDblClick,
24831         "contextmenu": this.onContextMenu,
24832         scope:this
24833     });
24834
24835     this.selections = [];
24836     this.nodes = [];
24837     this.cmp = new Roo.CompositeElementLite([]);
24838     if(this.store){
24839         this.store = Roo.factory(this.store, Roo.data);
24840         this.setStore(this.store, true);
24841     }
24842     
24843     if ( this.footer && this.footer.xtype) {
24844            
24845          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24846         
24847         this.footer.dataSource = this.store
24848         this.footer.container = fctr;
24849         this.footer = Roo.factory(this.footer, Roo);
24850         fctr.insertFirst(this.el);
24851         
24852         // this is a bit insane - as the paging toolbar seems to detach the el..
24853 //        dom.parentNode.parentNode.parentNode
24854          // they get detached?
24855     }
24856     
24857     
24858     Roo.View.superclass.constructor.call(this);
24859     
24860     
24861 };
24862
24863 Roo.extend(Roo.View, Roo.util.Observable, {
24864     
24865      /**
24866      * @cfg {Roo.data.Store} store Data store to load data from.
24867      */
24868     store : false,
24869     
24870     /**
24871      * @cfg {String|Roo.Element} el The container element.
24872      */
24873     el : '',
24874     
24875     /**
24876      * @cfg {String|Roo.Template} tpl The template used by this View 
24877      */
24878     tpl : false,
24879     /**
24880      * @cfg {String} dataName the named area of the template to use as the data area
24881      *                          Works with domtemplates roo-name="name"
24882      */
24883     dataName: false,
24884     /**
24885      * @cfg {String} selectedClass The css class to add to selected nodes
24886      */
24887     selectedClass : "x-view-selected",
24888      /**
24889      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24890      */
24891     emptyText : "",
24892     
24893     /**
24894      * @cfg {String} text to display on mask (default Loading)
24895      */
24896     mask : false,
24897     /**
24898      * @cfg {Boolean} multiSelect Allow multiple selection
24899      */
24900     multiSelect : false,
24901     /**
24902      * @cfg {Boolean} singleSelect Allow single selection
24903      */
24904     singleSelect:  false,
24905     
24906     /**
24907      * @cfg {Boolean} toggleSelect - selecting 
24908      */
24909     toggleSelect : false,
24910     
24911     /**
24912      * Returns the element this view is bound to.
24913      * @return {Roo.Element}
24914      */
24915     getEl : function(){
24916         return this.wrapEl;
24917     },
24918     
24919     
24920
24921     /**
24922      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24923      */
24924     refresh : function(){
24925         var t = this.tpl;
24926         
24927         // if we are using something like 'domtemplate', then
24928         // the what gets used is:
24929         // t.applySubtemplate(NAME, data, wrapping data..)
24930         // the outer template then get' applied with
24931         //     the store 'extra data'
24932         // and the body get's added to the
24933         //      roo-name="data" node?
24934         //      <span class='roo-tpl-{name}'></span> ?????
24935         
24936         
24937         
24938         this.clearSelections();
24939         this.el.update("");
24940         var html = [];
24941         var records = this.store.getRange();
24942         if(records.length < 1) {
24943             
24944             // is this valid??  = should it render a template??
24945             
24946             this.el.update(this.emptyText);
24947             return;
24948         }
24949         var el = this.el;
24950         if (this.dataName) {
24951             this.el.update(t.apply(this.store.meta)); //????
24952             el = this.el.child('.roo-tpl-' + this.dataName);
24953         }
24954         
24955         for(var i = 0, len = records.length; i < len; i++){
24956             var data = this.prepareData(records[i].data, i, records[i]);
24957             this.fireEvent("preparedata", this, data, i, records[i]);
24958             html[html.length] = Roo.util.Format.trim(
24959                 this.dataName ?
24960                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24961                     t.apply(data)
24962             );
24963         }
24964         
24965         
24966         
24967         el.update(html.join(""));
24968         this.nodes = el.dom.childNodes;
24969         this.updateIndexes(0);
24970     },
24971
24972     /**
24973      * Function to override to reformat the data that is sent to
24974      * the template for each node.
24975      * DEPRICATED - use the preparedata event handler.
24976      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24977      * a JSON object for an UpdateManager bound view).
24978      */
24979     prepareData : function(data, index, record)
24980     {
24981         this.fireEvent("preparedata", this, data, index, record);
24982         return data;
24983     },
24984
24985     onUpdate : function(ds, record){
24986         this.clearSelections();
24987         var index = this.store.indexOf(record);
24988         var n = this.nodes[index];
24989         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24990         n.parentNode.removeChild(n);
24991         this.updateIndexes(index, index);
24992     },
24993
24994     
24995     
24996 // --------- FIXME     
24997     onAdd : function(ds, records, index)
24998     {
24999         this.clearSelections();
25000         if(this.nodes.length == 0){
25001             this.refresh();
25002             return;
25003         }
25004         var n = this.nodes[index];
25005         for(var i = 0, len = records.length; i < len; i++){
25006             var d = this.prepareData(records[i].data, i, records[i]);
25007             if(n){
25008                 this.tpl.insertBefore(n, d);
25009             }else{
25010                 
25011                 this.tpl.append(this.el, d);
25012             }
25013         }
25014         this.updateIndexes(index);
25015     },
25016
25017     onRemove : function(ds, record, index){
25018         this.clearSelections();
25019         var el = this.dataName  ?
25020             this.el.child('.roo-tpl-' + this.dataName) :
25021             this.el; 
25022         el.dom.removeChild(this.nodes[index]);
25023         this.updateIndexes(index);
25024     },
25025
25026     /**
25027      * Refresh an individual node.
25028      * @param {Number} index
25029      */
25030     refreshNode : function(index){
25031         this.onUpdate(this.store, this.store.getAt(index));
25032     },
25033
25034     updateIndexes : function(startIndex, endIndex){
25035         var ns = this.nodes;
25036         startIndex = startIndex || 0;
25037         endIndex = endIndex || ns.length - 1;
25038         for(var i = startIndex; i <= endIndex; i++){
25039             ns[i].nodeIndex = i;
25040         }
25041     },
25042
25043     /**
25044      * Changes the data store this view uses and refresh the view.
25045      * @param {Store} store
25046      */
25047     setStore : function(store, initial){
25048         if(!initial && this.store){
25049             this.store.un("datachanged", this.refresh);
25050             this.store.un("add", this.onAdd);
25051             this.store.un("remove", this.onRemove);
25052             this.store.un("update", this.onUpdate);
25053             this.store.un("clear", this.refresh);
25054             this.store.un("beforeload", this.onBeforeLoad);
25055             this.store.un("load", this.onLoad);
25056             this.store.un("loadexception", this.onLoad);
25057         }
25058         if(store){
25059           
25060             store.on("datachanged", this.refresh, this);
25061             store.on("add", this.onAdd, this);
25062             store.on("remove", this.onRemove, this);
25063             store.on("update", this.onUpdate, this);
25064             store.on("clear", this.refresh, this);
25065             store.on("beforeload", this.onBeforeLoad, this);
25066             store.on("load", this.onLoad, this);
25067             store.on("loadexception", this.onLoad, this);
25068         }
25069         
25070         if(store){
25071             this.refresh();
25072         }
25073     },
25074     /**
25075      * onbeforeLoad - masks the loading area.
25076      *
25077      */
25078     onBeforeLoad : function()
25079     {
25080         this.el.update("");
25081         this.el.mask(this.mask ? this.mask : "Loading" ); 
25082     },
25083     onLoad : function ()
25084     {
25085         this.el.unmask();
25086     },
25087     
25088
25089     /**
25090      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25091      * @param {HTMLElement} node
25092      * @return {HTMLElement} The template node
25093      */
25094     findItemFromChild : function(node){
25095         var el = this.dataName  ?
25096             this.el.child('.roo-tpl-' + this.dataName,true) :
25097             this.el.dom; 
25098         
25099         if(!node || node.parentNode == el){
25100                     return node;
25101             }
25102             var p = node.parentNode;
25103             while(p && p != el){
25104             if(p.parentNode == el){
25105                 return p;
25106             }
25107             p = p.parentNode;
25108         }
25109             return null;
25110     },
25111
25112     /** @ignore */
25113     onClick : function(e){
25114         var item = this.findItemFromChild(e.getTarget());
25115         if(item){
25116             var index = this.indexOf(item);
25117             if(this.onItemClick(item, index, e) !== false){
25118                 this.fireEvent("click", this, index, item, e);
25119             }
25120         }else{
25121             this.clearSelections();
25122         }
25123     },
25124
25125     /** @ignore */
25126     onContextMenu : function(e){
25127         var item = this.findItemFromChild(e.getTarget());
25128         if(item){
25129             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25130         }
25131     },
25132
25133     /** @ignore */
25134     onDblClick : function(e){
25135         var item = this.findItemFromChild(e.getTarget());
25136         if(item){
25137             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25138         }
25139     },
25140
25141     onItemClick : function(item, index, e)
25142     {
25143         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25144             return false;
25145         }
25146         if (this.toggleSelect) {
25147             var m = this.isSelected(item) ? 'unselect' : 'select';
25148             Roo.log(m);
25149             var _t = this;
25150             _t[m](item, true, false);
25151             return true;
25152         }
25153         if(this.multiSelect || this.singleSelect){
25154             if(this.multiSelect && e.shiftKey && this.lastSelection){
25155                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25156             }else{
25157                 this.select(item, this.multiSelect && e.ctrlKey);
25158                 this.lastSelection = item;
25159             }
25160             e.preventDefault();
25161         }
25162         return true;
25163     },
25164
25165     /**
25166      * Get the number of selected nodes.
25167      * @return {Number}
25168      */
25169     getSelectionCount : function(){
25170         return this.selections.length;
25171     },
25172
25173     /**
25174      * Get the currently selected nodes.
25175      * @return {Array} An array of HTMLElements
25176      */
25177     getSelectedNodes : function(){
25178         return this.selections;
25179     },
25180
25181     /**
25182      * Get the indexes of the selected nodes.
25183      * @return {Array}
25184      */
25185     getSelectedIndexes : function(){
25186         var indexes = [], s = this.selections;
25187         for(var i = 0, len = s.length; i < len; i++){
25188             indexes.push(s[i].nodeIndex);
25189         }
25190         return indexes;
25191     },
25192
25193     /**
25194      * Clear all selections
25195      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25196      */
25197     clearSelections : function(suppressEvent){
25198         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25199             this.cmp.elements = this.selections;
25200             this.cmp.removeClass(this.selectedClass);
25201             this.selections = [];
25202             if(!suppressEvent){
25203                 this.fireEvent("selectionchange", this, this.selections);
25204             }
25205         }
25206     },
25207
25208     /**
25209      * Returns true if the passed node is selected
25210      * @param {HTMLElement/Number} node The node or node index
25211      * @return {Boolean}
25212      */
25213     isSelected : function(node){
25214         var s = this.selections;
25215         if(s.length < 1){
25216             return false;
25217         }
25218         node = this.getNode(node);
25219         return s.indexOf(node) !== -1;
25220     },
25221
25222     /**
25223      * Selects nodes.
25224      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25225      * @param {Boolean} keepExisting (optional) true to keep existing selections
25226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25227      */
25228     select : function(nodeInfo, keepExisting, suppressEvent){
25229         if(nodeInfo instanceof Array){
25230             if(!keepExisting){
25231                 this.clearSelections(true);
25232             }
25233             for(var i = 0, len = nodeInfo.length; i < len; i++){
25234                 this.select(nodeInfo[i], true, true);
25235             }
25236             return;
25237         } 
25238         var node = this.getNode(nodeInfo);
25239         if(!node || this.isSelected(node)){
25240             return; // already selected.
25241         }
25242         if(!keepExisting){
25243             this.clearSelections(true);
25244         }
25245         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25246             Roo.fly(node).addClass(this.selectedClass);
25247             this.selections.push(node);
25248             if(!suppressEvent){
25249                 this.fireEvent("selectionchange", this, this.selections);
25250             }
25251         }
25252         
25253         
25254     },
25255       /**
25256      * Unselects nodes.
25257      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25258      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25259      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25260      */
25261     unselect : function(nodeInfo, keepExisting, suppressEvent)
25262     {
25263         if(nodeInfo instanceof Array){
25264             Roo.each(this.selections, function(s) {
25265                 this.unselect(s, nodeInfo);
25266             }, this);
25267             return;
25268         }
25269         var node = this.getNode(nodeInfo);
25270         if(!node || !this.isSelected(node)){
25271             Roo.log("not selected");
25272             return; // not selected.
25273         }
25274         // fireevent???
25275         var ns = [];
25276         Roo.each(this.selections, function(s) {
25277             if (s == node ) {
25278                 Roo.fly(node).removeClass(this.selectedClass);
25279
25280                 return;
25281             }
25282             ns.push(s);
25283         },this);
25284         
25285         this.selections= ns;
25286         this.fireEvent("selectionchange", this, this.selections);
25287     },
25288
25289     /**
25290      * Gets a template node.
25291      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25292      * @return {HTMLElement} The node or null if it wasn't found
25293      */
25294     getNode : function(nodeInfo){
25295         if(typeof nodeInfo == "string"){
25296             return document.getElementById(nodeInfo);
25297         }else if(typeof nodeInfo == "number"){
25298             return this.nodes[nodeInfo];
25299         }
25300         return nodeInfo;
25301     },
25302
25303     /**
25304      * Gets a range template nodes.
25305      * @param {Number} startIndex
25306      * @param {Number} endIndex
25307      * @return {Array} An array of nodes
25308      */
25309     getNodes : function(start, end){
25310         var ns = this.nodes;
25311         start = start || 0;
25312         end = typeof end == "undefined" ? ns.length - 1 : end;
25313         var nodes = [];
25314         if(start <= end){
25315             for(var i = start; i <= end; i++){
25316                 nodes.push(ns[i]);
25317             }
25318         } else{
25319             for(var i = start; i >= end; i--){
25320                 nodes.push(ns[i]);
25321             }
25322         }
25323         return nodes;
25324     },
25325
25326     /**
25327      * Finds the index of the passed node
25328      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25329      * @return {Number} The index of the node or -1
25330      */
25331     indexOf : function(node){
25332         node = this.getNode(node);
25333         if(typeof node.nodeIndex == "number"){
25334             return node.nodeIndex;
25335         }
25336         var ns = this.nodes;
25337         for(var i = 0, len = ns.length; i < len; i++){
25338             if(ns[i] == node){
25339                 return i;
25340             }
25341         }
25342         return -1;
25343     }
25344 });
25345 /*
25346  * Based on:
25347  * Ext JS Library 1.1.1
25348  * Copyright(c) 2006-2007, Ext JS, LLC.
25349  *
25350  * Originally Released Under LGPL - original licence link has changed is not relivant.
25351  *
25352  * Fork - LGPL
25353  * <script type="text/javascript">
25354  */
25355
25356 /**
25357  * @class Roo.JsonView
25358  * @extends Roo.View
25359  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25360 <pre><code>
25361 var view = new Roo.JsonView({
25362     container: "my-element",
25363     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25364     multiSelect: true, 
25365     jsonRoot: "data" 
25366 });
25367
25368 // listen for node click?
25369 view.on("click", function(vw, index, node, e){
25370     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25371 });
25372
25373 // direct load of JSON data
25374 view.load("foobar.php");
25375
25376 // Example from my blog list
25377 var tpl = new Roo.Template(
25378     '&lt;div class="entry"&gt;' +
25379     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25380     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25381     "&lt;/div&gt;&lt;hr /&gt;"
25382 );
25383
25384 var moreView = new Roo.JsonView({
25385     container :  "entry-list", 
25386     template : tpl,
25387     jsonRoot: "posts"
25388 });
25389 moreView.on("beforerender", this.sortEntries, this);
25390 moreView.load({
25391     url: "/blog/get-posts.php",
25392     params: "allposts=true",
25393     text: "Loading Blog Entries..."
25394 });
25395 </code></pre>
25396
25397 * Note: old code is supported with arguments : (container, template, config)
25398
25399
25400  * @constructor
25401  * Create a new JsonView
25402  * 
25403  * @param {Object} config The config object
25404  * 
25405  */
25406 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25407     
25408     
25409     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25410
25411     var um = this.el.getUpdateManager();
25412     um.setRenderer(this);
25413     um.on("update", this.onLoad, this);
25414     um.on("failure", this.onLoadException, this);
25415
25416     /**
25417      * @event beforerender
25418      * Fires before rendering of the downloaded JSON data.
25419      * @param {Roo.JsonView} this
25420      * @param {Object} data The JSON data loaded
25421      */
25422     /**
25423      * @event load
25424      * Fires when data is loaded.
25425      * @param {Roo.JsonView} this
25426      * @param {Object} data The JSON data loaded
25427      * @param {Object} response The raw Connect response object
25428      */
25429     /**
25430      * @event loadexception
25431      * Fires when loading fails.
25432      * @param {Roo.JsonView} this
25433      * @param {Object} response The raw Connect response object
25434      */
25435     this.addEvents({
25436         'beforerender' : true,
25437         'load' : true,
25438         'loadexception' : true
25439     });
25440 };
25441 Roo.extend(Roo.JsonView, Roo.View, {
25442     /**
25443      * @type {String} The root property in the loaded JSON object that contains the data
25444      */
25445     jsonRoot : "",
25446
25447     /**
25448      * Refreshes the view.
25449      */
25450     refresh : function(){
25451         this.clearSelections();
25452         this.el.update("");
25453         var html = [];
25454         var o = this.jsonData;
25455         if(o && o.length > 0){
25456             for(var i = 0, len = o.length; i < len; i++){
25457                 var data = this.prepareData(o[i], i, o);
25458                 html[html.length] = this.tpl.apply(data);
25459             }
25460         }else{
25461             html.push(this.emptyText);
25462         }
25463         this.el.update(html.join(""));
25464         this.nodes = this.el.dom.childNodes;
25465         this.updateIndexes(0);
25466     },
25467
25468     /**
25469      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
25470      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
25471      <pre><code>
25472      view.load({
25473          url: "your-url.php",
25474          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25475          callback: yourFunction,
25476          scope: yourObject, //(optional scope)
25477          discardUrl: false,
25478          nocache: false,
25479          text: "Loading...",
25480          timeout: 30,
25481          scripts: false
25482      });
25483      </code></pre>
25484      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25485      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
25486      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
25487      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25488      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
25489      */
25490     load : function(){
25491         var um = this.el.getUpdateManager();
25492         um.update.apply(um, arguments);
25493     },
25494
25495     render : function(el, response){
25496         this.clearSelections();
25497         this.el.update("");
25498         var o;
25499         try{
25500             o = Roo.util.JSON.decode(response.responseText);
25501             if(this.jsonRoot){
25502                 
25503                 o = o[this.jsonRoot];
25504             }
25505         } catch(e){
25506         }
25507         /**
25508          * The current JSON data or null
25509          */
25510         this.jsonData = o;
25511         this.beforeRender();
25512         this.refresh();
25513     },
25514
25515 /**
25516  * Get the number of records in the current JSON dataset
25517  * @return {Number}
25518  */
25519     getCount : function(){
25520         return this.jsonData ? this.jsonData.length : 0;
25521     },
25522
25523 /**
25524  * Returns the JSON object for the specified node(s)
25525  * @param {HTMLElement/Array} node The node or an array of nodes
25526  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25527  * you get the JSON object for the node
25528  */
25529     getNodeData : function(node){
25530         if(node instanceof Array){
25531             var data = [];
25532             for(var i = 0, len = node.length; i < len; i++){
25533                 data.push(this.getNodeData(node[i]));
25534             }
25535             return data;
25536         }
25537         return this.jsonData[this.indexOf(node)] || null;
25538     },
25539
25540     beforeRender : function(){
25541         this.snapshot = this.jsonData;
25542         if(this.sortInfo){
25543             this.sort.apply(this, this.sortInfo);
25544         }
25545         this.fireEvent("beforerender", this, this.jsonData);
25546     },
25547
25548     onLoad : function(el, o){
25549         this.fireEvent("load", this, this.jsonData, o);
25550     },
25551
25552     onLoadException : function(el, o){
25553         this.fireEvent("loadexception", this, o);
25554     },
25555
25556 /**
25557  * Filter the data by a specific property.
25558  * @param {String} property A property on your JSON objects
25559  * @param {String/RegExp} value Either string that the property values
25560  * should start with, or a RegExp to test against the property
25561  */
25562     filter : function(property, value){
25563         if(this.jsonData){
25564             var data = [];
25565             var ss = this.snapshot;
25566             if(typeof value == "string"){
25567                 var vlen = value.length;
25568                 if(vlen == 0){
25569                     this.clearFilter();
25570                     return;
25571                 }
25572                 value = value.toLowerCase();
25573                 for(var i = 0, len = ss.length; i < len; i++){
25574                     var o = ss[i];
25575                     if(o[property].substr(0, vlen).toLowerCase() == value){
25576                         data.push(o);
25577                     }
25578                 }
25579             } else if(value.exec){ // regex?
25580                 for(var i = 0, len = ss.length; i < len; i++){
25581                     var o = ss[i];
25582                     if(value.test(o[property])){
25583                         data.push(o);
25584                     }
25585                 }
25586             } else{
25587                 return;
25588             }
25589             this.jsonData = data;
25590             this.refresh();
25591         }
25592     },
25593
25594 /**
25595  * Filter by a function. The passed function will be called with each
25596  * object in the current dataset. If the function returns true the value is kept,
25597  * otherwise it is filtered.
25598  * @param {Function} fn
25599  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25600  */
25601     filterBy : function(fn, scope){
25602         if(this.jsonData){
25603             var data = [];
25604             var ss = this.snapshot;
25605             for(var i = 0, len = ss.length; i < len; i++){
25606                 var o = ss[i];
25607                 if(fn.call(scope || this, o)){
25608                     data.push(o);
25609                 }
25610             }
25611             this.jsonData = data;
25612             this.refresh();
25613         }
25614     },
25615
25616 /**
25617  * Clears the current filter.
25618  */
25619     clearFilter : function(){
25620         if(this.snapshot && this.jsonData != this.snapshot){
25621             this.jsonData = this.snapshot;
25622             this.refresh();
25623         }
25624     },
25625
25626
25627 /**
25628  * Sorts the data for this view and refreshes it.
25629  * @param {String} property A property on your JSON objects to sort on
25630  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25631  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25632  */
25633     sort : function(property, dir, sortType){
25634         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25635         if(this.jsonData){
25636             var p = property;
25637             var dsc = dir && dir.toLowerCase() == "desc";
25638             var f = function(o1, o2){
25639                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25640                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25641                 ;
25642                 if(v1 < v2){
25643                     return dsc ? +1 : -1;
25644                 } else if(v1 > v2){
25645                     return dsc ? -1 : +1;
25646                 } else{
25647                     return 0;
25648                 }
25649             };
25650             this.jsonData.sort(f);
25651             this.refresh();
25652             if(this.jsonData != this.snapshot){
25653                 this.snapshot.sort(f);
25654             }
25655         }
25656     }
25657 });/*
25658  * Based on:
25659  * Ext JS Library 1.1.1
25660  * Copyright(c) 2006-2007, Ext JS, LLC.
25661  *
25662  * Originally Released Under LGPL - original licence link has changed is not relivant.
25663  *
25664  * Fork - LGPL
25665  * <script type="text/javascript">
25666  */
25667  
25668
25669 /**
25670  * @class Roo.ColorPalette
25671  * @extends Roo.Component
25672  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25673  * Here's an example of typical usage:
25674  * <pre><code>
25675 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25676 cp.render('my-div');
25677
25678 cp.on('select', function(palette, selColor){
25679     // do something with selColor
25680 });
25681 </code></pre>
25682  * @constructor
25683  * Create a new ColorPalette
25684  * @param {Object} config The config object
25685  */
25686 Roo.ColorPalette = function(config){
25687     Roo.ColorPalette.superclass.constructor.call(this, config);
25688     this.addEvents({
25689         /**
25690              * @event select
25691              * Fires when a color is selected
25692              * @param {ColorPalette} this
25693              * @param {String} color The 6-digit color hex code (without the # symbol)
25694              */
25695         select: true
25696     });
25697
25698     if(this.handler){
25699         this.on("select", this.handler, this.scope, true);
25700     }
25701 };
25702 Roo.extend(Roo.ColorPalette, Roo.Component, {
25703     /**
25704      * @cfg {String} itemCls
25705      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25706      */
25707     itemCls : "x-color-palette",
25708     /**
25709      * @cfg {String} value
25710      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25711      * the hex codes are case-sensitive.
25712      */
25713     value : null,
25714     clickEvent:'click',
25715     // private
25716     ctype: "Roo.ColorPalette",
25717
25718     /**
25719      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25720      */
25721     allowReselect : false,
25722
25723     /**
25724      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25725      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25726      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25727      * of colors with the width setting until the box is symmetrical.</p>
25728      * <p>You can override individual colors if needed:</p>
25729      * <pre><code>
25730 var cp = new Roo.ColorPalette();
25731 cp.colors[0] = "FF0000";  // change the first box to red
25732 </code></pre>
25733
25734 Or you can provide a custom array of your own for complete control:
25735 <pre><code>
25736 var cp = new Roo.ColorPalette();
25737 cp.colors = ["000000", "993300", "333300"];
25738 </code></pre>
25739      * @type Array
25740      */
25741     colors : [
25742         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25743         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25744         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25745         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25746         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25747     ],
25748
25749     // private
25750     onRender : function(container, position){
25751         var t = new Roo.MasterTemplate(
25752             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25753         );
25754         var c = this.colors;
25755         for(var i = 0, len = c.length; i < len; i++){
25756             t.add([c[i]]);
25757         }
25758         var el = document.createElement("div");
25759         el.className = this.itemCls;
25760         t.overwrite(el);
25761         container.dom.insertBefore(el, position);
25762         this.el = Roo.get(el);
25763         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25764         if(this.clickEvent != 'click'){
25765             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25766         }
25767     },
25768
25769     // private
25770     afterRender : function(){
25771         Roo.ColorPalette.superclass.afterRender.call(this);
25772         if(this.value){
25773             var s = this.value;
25774             this.value = null;
25775             this.select(s);
25776         }
25777     },
25778
25779     // private
25780     handleClick : function(e, t){
25781         e.preventDefault();
25782         if(!this.disabled){
25783             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25784             this.select(c.toUpperCase());
25785         }
25786     },
25787
25788     /**
25789      * Selects the specified color in the palette (fires the select event)
25790      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25791      */
25792     select : function(color){
25793         color = color.replace("#", "");
25794         if(color != this.value || this.allowReselect){
25795             var el = this.el;
25796             if(this.value){
25797                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25798             }
25799             el.child("a.color-"+color).addClass("x-color-palette-sel");
25800             this.value = color;
25801             this.fireEvent("select", this, color);
25802         }
25803     }
25804 });/*
25805  * Based on:
25806  * Ext JS Library 1.1.1
25807  * Copyright(c) 2006-2007, Ext JS, LLC.
25808  *
25809  * Originally Released Under LGPL - original licence link has changed is not relivant.
25810  *
25811  * Fork - LGPL
25812  * <script type="text/javascript">
25813  */
25814  
25815 /**
25816  * @class Roo.DatePicker
25817  * @extends Roo.Component
25818  * Simple date picker class.
25819  * @constructor
25820  * Create a new DatePicker
25821  * @param {Object} config The config object
25822  */
25823 Roo.DatePicker = function(config){
25824     Roo.DatePicker.superclass.constructor.call(this, config);
25825
25826     this.value = config && config.value ?
25827                  config.value.clearTime() : new Date().clearTime();
25828
25829     this.addEvents({
25830         /**
25831              * @event select
25832              * Fires when a date is selected
25833              * @param {DatePicker} this
25834              * @param {Date} date The selected date
25835              */
25836         'select': true,
25837         /**
25838              * @event monthchange
25839              * Fires when the displayed month changes 
25840              * @param {DatePicker} this
25841              * @param {Date} date The selected month
25842              */
25843         'monthchange': true
25844     });
25845
25846     if(this.handler){
25847         this.on("select", this.handler,  this.scope || this);
25848     }
25849     // build the disabledDatesRE
25850     if(!this.disabledDatesRE && this.disabledDates){
25851         var dd = this.disabledDates;
25852         var re = "(?:";
25853         for(var i = 0; i < dd.length; i++){
25854             re += dd[i];
25855             if(i != dd.length-1) re += "|";
25856         }
25857         this.disabledDatesRE = new RegExp(re + ")");
25858     }
25859 };
25860
25861 Roo.extend(Roo.DatePicker, Roo.Component, {
25862     /**
25863      * @cfg {String} todayText
25864      * The text to display on the button that selects the current date (defaults to "Today")
25865      */
25866     todayText : "Today",
25867     /**
25868      * @cfg {String} okText
25869      * The text to display on the ok button
25870      */
25871     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25872     /**
25873      * @cfg {String} cancelText
25874      * The text to display on the cancel button
25875      */
25876     cancelText : "Cancel",
25877     /**
25878      * @cfg {String} todayTip
25879      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25880      */
25881     todayTip : "{0} (Spacebar)",
25882     /**
25883      * @cfg {Date} minDate
25884      * Minimum allowable date (JavaScript date object, defaults to null)
25885      */
25886     minDate : null,
25887     /**
25888      * @cfg {Date} maxDate
25889      * Maximum allowable date (JavaScript date object, defaults to null)
25890      */
25891     maxDate : null,
25892     /**
25893      * @cfg {String} minText
25894      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25895      */
25896     minText : "This date is before the minimum date",
25897     /**
25898      * @cfg {String} maxText
25899      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25900      */
25901     maxText : "This date is after the maximum date",
25902     /**
25903      * @cfg {String} format
25904      * The default date format string which can be overriden for localization support.  The format must be
25905      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25906      */
25907     format : "m/d/y",
25908     /**
25909      * @cfg {Array} disabledDays
25910      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25911      */
25912     disabledDays : null,
25913     /**
25914      * @cfg {String} disabledDaysText
25915      * The tooltip to display when the date falls on a disabled day (defaults to "")
25916      */
25917     disabledDaysText : "",
25918     /**
25919      * @cfg {RegExp} disabledDatesRE
25920      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25921      */
25922     disabledDatesRE : null,
25923     /**
25924      * @cfg {String} disabledDatesText
25925      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25926      */
25927     disabledDatesText : "",
25928     /**
25929      * @cfg {Boolean} constrainToViewport
25930      * True to constrain the date picker to the viewport (defaults to true)
25931      */
25932     constrainToViewport : true,
25933     /**
25934      * @cfg {Array} monthNames
25935      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25936      */
25937     monthNames : Date.monthNames,
25938     /**
25939      * @cfg {Array} dayNames
25940      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25941      */
25942     dayNames : Date.dayNames,
25943     /**
25944      * @cfg {String} nextText
25945      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25946      */
25947     nextText: 'Next Month (Control+Right)',
25948     /**
25949      * @cfg {String} prevText
25950      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25951      */
25952     prevText: 'Previous Month (Control+Left)',
25953     /**
25954      * @cfg {String} monthYearText
25955      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25956      */
25957     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25958     /**
25959      * @cfg {Number} startDay
25960      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25961      */
25962     startDay : 0,
25963     /**
25964      * @cfg {Bool} showClear
25965      * Show a clear button (usefull for date form elements that can be blank.)
25966      */
25967     
25968     showClear: false,
25969     
25970     /**
25971      * Sets the value of the date field
25972      * @param {Date} value The date to set
25973      */
25974     setValue : function(value){
25975         var old = this.value;
25976         
25977         if (typeof(value) == 'string') {
25978          
25979             value = Date.parseDate(value, this.format);
25980         }
25981         if (!value) {
25982             value = new Date();
25983         }
25984         
25985         this.value = value.clearTime(true);
25986         if(this.el){
25987             this.update(this.value);
25988         }
25989     },
25990
25991     /**
25992      * Gets the current selected value of the date field
25993      * @return {Date} The selected date
25994      */
25995     getValue : function(){
25996         return this.value;
25997     },
25998
25999     // private
26000     focus : function(){
26001         if(this.el){
26002             this.update(this.activeDate);
26003         }
26004     },
26005
26006     // privateval
26007     onRender : function(container, position){
26008         
26009         var m = [
26010              '<table cellspacing="0">',
26011                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
26012                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26013         var dn = this.dayNames;
26014         for(var i = 0; i < 7; i++){
26015             var d = this.startDay+i;
26016             if(d > 6){
26017                 d = d-7;
26018             }
26019             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26020         }
26021         m[m.length] = "</tr></thead><tbody><tr>";
26022         for(var i = 0; i < 42; i++) {
26023             if(i % 7 == 0 && i != 0){
26024                 m[m.length] = "</tr><tr>";
26025             }
26026             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26027         }
26028         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26029             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26030
26031         var el = document.createElement("div");
26032         el.className = "x-date-picker";
26033         el.innerHTML = m.join("");
26034
26035         container.dom.insertBefore(el, position);
26036
26037         this.el = Roo.get(el);
26038         this.eventEl = Roo.get(el.firstChild);
26039
26040         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26041             handler: this.showPrevMonth,
26042             scope: this,
26043             preventDefault:true,
26044             stopDefault:true
26045         });
26046
26047         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26048             handler: this.showNextMonth,
26049             scope: this,
26050             preventDefault:true,
26051             stopDefault:true
26052         });
26053
26054         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26055
26056         this.monthPicker = this.el.down('div.x-date-mp');
26057         this.monthPicker.enableDisplayMode('block');
26058         
26059         var kn = new Roo.KeyNav(this.eventEl, {
26060             "left" : function(e){
26061                 e.ctrlKey ?
26062                     this.showPrevMonth() :
26063                     this.update(this.activeDate.add("d", -1));
26064             },
26065
26066             "right" : function(e){
26067                 e.ctrlKey ?
26068                     this.showNextMonth() :
26069                     this.update(this.activeDate.add("d", 1));
26070             },
26071
26072             "up" : function(e){
26073                 e.ctrlKey ?
26074                     this.showNextYear() :
26075                     this.update(this.activeDate.add("d", -7));
26076             },
26077
26078             "down" : function(e){
26079                 e.ctrlKey ?
26080                     this.showPrevYear() :
26081                     this.update(this.activeDate.add("d", 7));
26082             },
26083
26084             "pageUp" : function(e){
26085                 this.showNextMonth();
26086             },
26087
26088             "pageDown" : function(e){
26089                 this.showPrevMonth();
26090             },
26091
26092             "enter" : function(e){
26093                 e.stopPropagation();
26094                 return true;
26095             },
26096
26097             scope : this
26098         });
26099
26100         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26101
26102         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26103
26104         this.el.unselectable();
26105         
26106         this.cells = this.el.select("table.x-date-inner tbody td");
26107         this.textNodes = this.el.query("table.x-date-inner tbody span");
26108
26109         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26110             text: "&#160;",
26111             tooltip: this.monthYearText
26112         });
26113
26114         this.mbtn.on('click', this.showMonthPicker, this);
26115         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26116
26117
26118         var today = (new Date()).dateFormat(this.format);
26119         
26120         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26121         if (this.showClear) {
26122             baseTb.add( new Roo.Toolbar.Fill());
26123         }
26124         baseTb.add({
26125             text: String.format(this.todayText, today),
26126             tooltip: String.format(this.todayTip, today),
26127             handler: this.selectToday,
26128             scope: this
26129         });
26130         
26131         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26132             
26133         //});
26134         if (this.showClear) {
26135             
26136             baseTb.add( new Roo.Toolbar.Fill());
26137             baseTb.add({
26138                 text: '&#160;',
26139                 cls: 'x-btn-icon x-btn-clear',
26140                 handler: function() {
26141                     //this.value = '';
26142                     this.fireEvent("select", this, '');
26143                 },
26144                 scope: this
26145             });
26146         }
26147         
26148         
26149         if(Roo.isIE){
26150             this.el.repaint();
26151         }
26152         this.update(this.value);
26153     },
26154
26155     createMonthPicker : function(){
26156         if(!this.monthPicker.dom.firstChild){
26157             var buf = ['<table border="0" cellspacing="0">'];
26158             for(var i = 0; i < 6; i++){
26159                 buf.push(
26160                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26161                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26162                     i == 0 ?
26163                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
26164                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26165                 );
26166             }
26167             buf.push(
26168                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26169                     this.okText,
26170                     '</button><button type="button" class="x-date-mp-cancel">',
26171                     this.cancelText,
26172                     '</button></td></tr>',
26173                 '</table>'
26174             );
26175             this.monthPicker.update(buf.join(''));
26176             this.monthPicker.on('click', this.onMonthClick, this);
26177             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26178
26179             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26180             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26181
26182             this.mpMonths.each(function(m, a, i){
26183                 i += 1;
26184                 if((i%2) == 0){
26185                     m.dom.xmonth = 5 + Math.round(i * .5);
26186                 }else{
26187                     m.dom.xmonth = Math.round((i-1) * .5);
26188                 }
26189             });
26190         }
26191     },
26192
26193     showMonthPicker : function(){
26194         this.createMonthPicker();
26195         var size = this.el.getSize();
26196         this.monthPicker.setSize(size);
26197         this.monthPicker.child('table').setSize(size);
26198
26199         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26200         this.updateMPMonth(this.mpSelMonth);
26201         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26202         this.updateMPYear(this.mpSelYear);
26203
26204         this.monthPicker.slideIn('t', {duration:.2});
26205     },
26206
26207     updateMPYear : function(y){
26208         this.mpyear = y;
26209         var ys = this.mpYears.elements;
26210         for(var i = 1; i <= 10; i++){
26211             var td = ys[i-1], y2;
26212             if((i%2) == 0){
26213                 y2 = y + Math.round(i * .5);
26214                 td.firstChild.innerHTML = y2;
26215                 td.xyear = y2;
26216             }else{
26217                 y2 = y - (5-Math.round(i * .5));
26218                 td.firstChild.innerHTML = y2;
26219                 td.xyear = y2;
26220             }
26221             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26222         }
26223     },
26224
26225     updateMPMonth : function(sm){
26226         this.mpMonths.each(function(m, a, i){
26227             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26228         });
26229     },
26230
26231     selectMPMonth: function(m){
26232         
26233     },
26234
26235     onMonthClick : function(e, t){
26236         e.stopEvent();
26237         var el = new Roo.Element(t), pn;
26238         if(el.is('button.x-date-mp-cancel')){
26239             this.hideMonthPicker();
26240         }
26241         else if(el.is('button.x-date-mp-ok')){
26242             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26243             this.hideMonthPicker();
26244         }
26245         else if(pn = el.up('td.x-date-mp-month', 2)){
26246             this.mpMonths.removeClass('x-date-mp-sel');
26247             pn.addClass('x-date-mp-sel');
26248             this.mpSelMonth = pn.dom.xmonth;
26249         }
26250         else if(pn = el.up('td.x-date-mp-year', 2)){
26251             this.mpYears.removeClass('x-date-mp-sel');
26252             pn.addClass('x-date-mp-sel');
26253             this.mpSelYear = pn.dom.xyear;
26254         }
26255         else if(el.is('a.x-date-mp-prev')){
26256             this.updateMPYear(this.mpyear-10);
26257         }
26258         else if(el.is('a.x-date-mp-next')){
26259             this.updateMPYear(this.mpyear+10);
26260         }
26261     },
26262
26263     onMonthDblClick : function(e, t){
26264         e.stopEvent();
26265         var el = new Roo.Element(t), pn;
26266         if(pn = el.up('td.x-date-mp-month', 2)){
26267             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26268             this.hideMonthPicker();
26269         }
26270         else if(pn = el.up('td.x-date-mp-year', 2)){
26271             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26272             this.hideMonthPicker();
26273         }
26274     },
26275
26276     hideMonthPicker : function(disableAnim){
26277         if(this.monthPicker){
26278             if(disableAnim === true){
26279                 this.monthPicker.hide();
26280             }else{
26281                 this.monthPicker.slideOut('t', {duration:.2});
26282             }
26283         }
26284     },
26285
26286     // private
26287     showPrevMonth : function(e){
26288         this.update(this.activeDate.add("mo", -1));
26289     },
26290
26291     // private
26292     showNextMonth : function(e){
26293         this.update(this.activeDate.add("mo", 1));
26294     },
26295
26296     // private
26297     showPrevYear : function(){
26298         this.update(this.activeDate.add("y", -1));
26299     },
26300
26301     // private
26302     showNextYear : function(){
26303         this.update(this.activeDate.add("y", 1));
26304     },
26305
26306     // private
26307     handleMouseWheel : function(e){
26308         var delta = e.getWheelDelta();
26309         if(delta > 0){
26310             this.showPrevMonth();
26311             e.stopEvent();
26312         } else if(delta < 0){
26313             this.showNextMonth();
26314             e.stopEvent();
26315         }
26316     },
26317
26318     // private
26319     handleDateClick : function(e, t){
26320         e.stopEvent();
26321         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26322             this.setValue(new Date(t.dateValue));
26323             this.fireEvent("select", this, this.value);
26324         }
26325     },
26326
26327     // private
26328     selectToday : function(){
26329         this.setValue(new Date().clearTime());
26330         this.fireEvent("select", this, this.value);
26331     },
26332
26333     // private
26334     update : function(date)
26335     {
26336         var vd = this.activeDate;
26337         this.activeDate = date;
26338         if(vd && this.el){
26339             var t = date.getTime();
26340             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26341                 this.cells.removeClass("x-date-selected");
26342                 this.cells.each(function(c){
26343                    if(c.dom.firstChild.dateValue == t){
26344                        c.addClass("x-date-selected");
26345                        setTimeout(function(){
26346                             try{c.dom.firstChild.focus();}catch(e){}
26347                        }, 50);
26348                        return false;
26349                    }
26350                 });
26351                 return;
26352             }
26353         }
26354         
26355         var days = date.getDaysInMonth();
26356         var firstOfMonth = date.getFirstDateOfMonth();
26357         var startingPos = firstOfMonth.getDay()-this.startDay;
26358
26359         if(startingPos <= this.startDay){
26360             startingPos += 7;
26361         }
26362
26363         var pm = date.add("mo", -1);
26364         var prevStart = pm.getDaysInMonth()-startingPos;
26365
26366         var cells = this.cells.elements;
26367         var textEls = this.textNodes;
26368         days += startingPos;
26369
26370         // convert everything to numbers so it's fast
26371         var day = 86400000;
26372         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26373         var today = new Date().clearTime().getTime();
26374         var sel = date.clearTime().getTime();
26375         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26376         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26377         var ddMatch = this.disabledDatesRE;
26378         var ddText = this.disabledDatesText;
26379         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26380         var ddaysText = this.disabledDaysText;
26381         var format = this.format;
26382
26383         var setCellClass = function(cal, cell){
26384             cell.title = "";
26385             var t = d.getTime();
26386             cell.firstChild.dateValue = t;
26387             if(t == today){
26388                 cell.className += " x-date-today";
26389                 cell.title = cal.todayText;
26390             }
26391             if(t == sel){
26392                 cell.className += " x-date-selected";
26393                 setTimeout(function(){
26394                     try{cell.firstChild.focus();}catch(e){}
26395                 }, 50);
26396             }
26397             // disabling
26398             if(t < min) {
26399                 cell.className = " x-date-disabled";
26400                 cell.title = cal.minText;
26401                 return;
26402             }
26403             if(t > max) {
26404                 cell.className = " x-date-disabled";
26405                 cell.title = cal.maxText;
26406                 return;
26407             }
26408             if(ddays){
26409                 if(ddays.indexOf(d.getDay()) != -1){
26410                     cell.title = ddaysText;
26411                     cell.className = " x-date-disabled";
26412                 }
26413             }
26414             if(ddMatch && format){
26415                 var fvalue = d.dateFormat(format);
26416                 if(ddMatch.test(fvalue)){
26417                     cell.title = ddText.replace("%0", fvalue);
26418                     cell.className = " x-date-disabled";
26419                 }
26420             }
26421         };
26422
26423         var i = 0;
26424         for(; i < startingPos; i++) {
26425             textEls[i].innerHTML = (++prevStart);
26426             d.setDate(d.getDate()+1);
26427             cells[i].className = "x-date-prevday";
26428             setCellClass(this, cells[i]);
26429         }
26430         for(; i < days; i++){
26431             intDay = i - startingPos + 1;
26432             textEls[i].innerHTML = (intDay);
26433             d.setDate(d.getDate()+1);
26434             cells[i].className = "x-date-active";
26435             setCellClass(this, cells[i]);
26436         }
26437         var extraDays = 0;
26438         for(; i < 42; i++) {
26439              textEls[i].innerHTML = (++extraDays);
26440              d.setDate(d.getDate()+1);
26441              cells[i].className = "x-date-nextday";
26442              setCellClass(this, cells[i]);
26443         }
26444
26445         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26446         this.fireEvent('monthchange', this, date);
26447         
26448         if(!this.internalRender){
26449             var main = this.el.dom.firstChild;
26450             var w = main.offsetWidth;
26451             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26452             Roo.fly(main).setWidth(w);
26453             this.internalRender = true;
26454             // opera does not respect the auto grow header center column
26455             // then, after it gets a width opera refuses to recalculate
26456             // without a second pass
26457             if(Roo.isOpera && !this.secondPass){
26458                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26459                 this.secondPass = true;
26460                 this.update.defer(10, this, [date]);
26461             }
26462         }
26463         
26464         
26465     }
26466 });        /*
26467  * Based on:
26468  * Ext JS Library 1.1.1
26469  * Copyright(c) 2006-2007, Ext JS, LLC.
26470  *
26471  * Originally Released Under LGPL - original licence link has changed is not relivant.
26472  *
26473  * Fork - LGPL
26474  * <script type="text/javascript">
26475  */
26476 /**
26477  * @class Roo.TabPanel
26478  * @extends Roo.util.Observable
26479  * A lightweight tab container.
26480  * <br><br>
26481  * Usage:
26482  * <pre><code>
26483 // basic tabs 1, built from existing content
26484 var tabs = new Roo.TabPanel("tabs1");
26485 tabs.addTab("script", "View Script");
26486 tabs.addTab("markup", "View Markup");
26487 tabs.activate("script");
26488
26489 // more advanced tabs, built from javascript
26490 var jtabs = new Roo.TabPanel("jtabs");
26491 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26492
26493 // set up the UpdateManager
26494 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26495 var updater = tab2.getUpdateManager();
26496 updater.setDefaultUrl("ajax1.htm");
26497 tab2.on('activate', updater.refresh, updater, true);
26498
26499 // Use setUrl for Ajax loading
26500 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26501 tab3.setUrl("ajax2.htm", null, true);
26502
26503 // Disabled tab
26504 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26505 tab4.disable();
26506
26507 jtabs.activate("jtabs-1");
26508  * </code></pre>
26509  * @constructor
26510  * Create a new TabPanel.
26511  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26512  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26513  */
26514 Roo.TabPanel = function(container, config){
26515     /**
26516     * The container element for this TabPanel.
26517     * @type Roo.Element
26518     */
26519     this.el = Roo.get(container, true);
26520     if(config){
26521         if(typeof config == "boolean"){
26522             this.tabPosition = config ? "bottom" : "top";
26523         }else{
26524             Roo.apply(this, config);
26525         }
26526     }
26527     if(this.tabPosition == "bottom"){
26528         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26529         this.el.addClass("x-tabs-bottom");
26530     }
26531     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26532     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26533     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26534     if(Roo.isIE){
26535         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26536     }
26537     if(this.tabPosition != "bottom"){
26538         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26539          * @type Roo.Element
26540          */
26541         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26542         this.el.addClass("x-tabs-top");
26543     }
26544     this.items = [];
26545
26546     this.bodyEl.setStyle("position", "relative");
26547
26548     this.active = null;
26549     this.activateDelegate = this.activate.createDelegate(this);
26550
26551     this.addEvents({
26552         /**
26553          * @event tabchange
26554          * Fires when the active tab changes
26555          * @param {Roo.TabPanel} this
26556          * @param {Roo.TabPanelItem} activePanel The new active tab
26557          */
26558         "tabchange": true,
26559         /**
26560          * @event beforetabchange
26561          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26562          * @param {Roo.TabPanel} this
26563          * @param {Object} e Set cancel to true on this object to cancel the tab change
26564          * @param {Roo.TabPanelItem} tab The tab being changed to
26565          */
26566         "beforetabchange" : true
26567     });
26568
26569     Roo.EventManager.onWindowResize(this.onResize, this);
26570     this.cpad = this.el.getPadding("lr");
26571     this.hiddenCount = 0;
26572
26573
26574     // toolbar on the tabbar support...
26575     if (this.toolbar) {
26576         var tcfg = this.toolbar;
26577         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26578         this.toolbar = new Roo.Toolbar(tcfg);
26579         if (Roo.isSafari) {
26580             var tbl = tcfg.container.child('table', true);
26581             tbl.setAttribute('width', '100%');
26582         }
26583         
26584     }
26585    
26586
26587
26588     Roo.TabPanel.superclass.constructor.call(this);
26589 };
26590
26591 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26592     /*
26593      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26594      */
26595     tabPosition : "top",
26596     /*
26597      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26598      */
26599     currentTabWidth : 0,
26600     /*
26601      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26602      */
26603     minTabWidth : 40,
26604     /*
26605      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26606      */
26607     maxTabWidth : 250,
26608     /*
26609      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26610      */
26611     preferredTabWidth : 175,
26612     /*
26613      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26614      */
26615     resizeTabs : false,
26616     /*
26617      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26618      */
26619     monitorResize : true,
26620     /*
26621      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26622      */
26623     toolbar : false,
26624
26625     /**
26626      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26627      * @param {String} id The id of the div to use <b>or create</b>
26628      * @param {String} text The text for the tab
26629      * @param {String} content (optional) Content to put in the TabPanelItem body
26630      * @param {Boolean} closable (optional) True to create a close icon on the tab
26631      * @return {Roo.TabPanelItem} The created TabPanelItem
26632      */
26633     addTab : function(id, text, content, closable){
26634         var item = new Roo.TabPanelItem(this, id, text, closable);
26635         this.addTabItem(item);
26636         if(content){
26637             item.setContent(content);
26638         }
26639         return item;
26640     },
26641
26642     /**
26643      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26644      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26645      * @return {Roo.TabPanelItem}
26646      */
26647     getTab : function(id){
26648         return this.items[id];
26649     },
26650
26651     /**
26652      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26653      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26654      */
26655     hideTab : function(id){
26656         var t = this.items[id];
26657         if(!t.isHidden()){
26658            t.setHidden(true);
26659            this.hiddenCount++;
26660            this.autoSizeTabs();
26661         }
26662     },
26663
26664     /**
26665      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26666      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26667      */
26668     unhideTab : function(id){
26669         var t = this.items[id];
26670         if(t.isHidden()){
26671            t.setHidden(false);
26672            this.hiddenCount--;
26673            this.autoSizeTabs();
26674         }
26675     },
26676
26677     /**
26678      * Adds an existing {@link Roo.TabPanelItem}.
26679      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26680      */
26681     addTabItem : function(item){
26682         this.items[item.id] = item;
26683         this.items.push(item);
26684         if(this.resizeTabs){
26685            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26686            this.autoSizeTabs();
26687         }else{
26688             item.autoSize();
26689         }
26690     },
26691
26692     /**
26693      * Removes a {@link Roo.TabPanelItem}.
26694      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26695      */
26696     removeTab : function(id){
26697         var items = this.items;
26698         var tab = items[id];
26699         if(!tab) { return; }
26700         var index = items.indexOf(tab);
26701         if(this.active == tab && items.length > 1){
26702             var newTab = this.getNextAvailable(index);
26703             if(newTab) {
26704                 newTab.activate();
26705             }
26706         }
26707         this.stripEl.dom.removeChild(tab.pnode.dom);
26708         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26709             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26710         }
26711         items.splice(index, 1);
26712         delete this.items[tab.id];
26713         tab.fireEvent("close", tab);
26714         tab.purgeListeners();
26715         this.autoSizeTabs();
26716     },
26717
26718     getNextAvailable : function(start){
26719         var items = this.items;
26720         var index = start;
26721         // look for a next tab that will slide over to
26722         // replace the one being removed
26723         while(index < items.length){
26724             var item = items[++index];
26725             if(item && !item.isHidden()){
26726                 return item;
26727             }
26728         }
26729         // if one isn't found select the previous tab (on the left)
26730         index = start;
26731         while(index >= 0){
26732             var item = items[--index];
26733             if(item && !item.isHidden()){
26734                 return item;
26735             }
26736         }
26737         return null;
26738     },
26739
26740     /**
26741      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26742      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26743      */
26744     disableTab : function(id){
26745         var tab = this.items[id];
26746         if(tab && this.active != tab){
26747             tab.disable();
26748         }
26749     },
26750
26751     /**
26752      * Enables a {@link Roo.TabPanelItem} that is disabled.
26753      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26754      */
26755     enableTab : function(id){
26756         var tab = this.items[id];
26757         tab.enable();
26758     },
26759
26760     /**
26761      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26762      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26763      * @return {Roo.TabPanelItem} The TabPanelItem.
26764      */
26765     activate : function(id){
26766         var tab = this.items[id];
26767         if(!tab){
26768             return null;
26769         }
26770         if(tab == this.active || tab.disabled){
26771             return tab;
26772         }
26773         var e = {};
26774         this.fireEvent("beforetabchange", this, e, tab);
26775         if(e.cancel !== true && !tab.disabled){
26776             if(this.active){
26777                 this.active.hide();
26778             }
26779             this.active = this.items[id];
26780             this.active.show();
26781             this.fireEvent("tabchange", this, this.active);
26782         }
26783         return tab;
26784     },
26785
26786     /**
26787      * Gets the active {@link Roo.TabPanelItem}.
26788      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26789      */
26790     getActiveTab : function(){
26791         return this.active;
26792     },
26793
26794     /**
26795      * Updates the tab body element to fit the height of the container element
26796      * for overflow scrolling
26797      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26798      */
26799     syncHeight : function(targetHeight){
26800         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26801         var bm = this.bodyEl.getMargins();
26802         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26803         this.bodyEl.setHeight(newHeight);
26804         return newHeight;
26805     },
26806
26807     onResize : function(){
26808         if(this.monitorResize){
26809             this.autoSizeTabs();
26810         }
26811     },
26812
26813     /**
26814      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26815      */
26816     beginUpdate : function(){
26817         this.updating = true;
26818     },
26819
26820     /**
26821      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26822      */
26823     endUpdate : function(){
26824         this.updating = false;
26825         this.autoSizeTabs();
26826     },
26827
26828     /**
26829      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26830      */
26831     autoSizeTabs : function(){
26832         var count = this.items.length;
26833         var vcount = count - this.hiddenCount;
26834         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26835         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26836         var availWidth = Math.floor(w / vcount);
26837         var b = this.stripBody;
26838         if(b.getWidth() > w){
26839             var tabs = this.items;
26840             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26841             if(availWidth < this.minTabWidth){
26842                 /*if(!this.sleft){    // incomplete scrolling code
26843                     this.createScrollButtons();
26844                 }
26845                 this.showScroll();
26846                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26847             }
26848         }else{
26849             if(this.currentTabWidth < this.preferredTabWidth){
26850                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26851             }
26852         }
26853     },
26854
26855     /**
26856      * Returns the number of tabs in this TabPanel.
26857      * @return {Number}
26858      */
26859      getCount : function(){
26860          return this.items.length;
26861      },
26862
26863     /**
26864      * Resizes all the tabs to the passed width
26865      * @param {Number} The new width
26866      */
26867     setTabWidth : function(width){
26868         this.currentTabWidth = width;
26869         for(var i = 0, len = this.items.length; i < len; i++) {
26870                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26871         }
26872     },
26873
26874     /**
26875      * Destroys this TabPanel
26876      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26877      */
26878     destroy : function(removeEl){
26879         Roo.EventManager.removeResizeListener(this.onResize, this);
26880         for(var i = 0, len = this.items.length; i < len; i++){
26881             this.items[i].purgeListeners();
26882         }
26883         if(removeEl === true){
26884             this.el.update("");
26885             this.el.remove();
26886         }
26887     }
26888 });
26889
26890 /**
26891  * @class Roo.TabPanelItem
26892  * @extends Roo.util.Observable
26893  * Represents an individual item (tab plus body) in a TabPanel.
26894  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26895  * @param {String} id The id of this TabPanelItem
26896  * @param {String} text The text for the tab of this TabPanelItem
26897  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26898  */
26899 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26900     /**
26901      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26902      * @type Roo.TabPanel
26903      */
26904     this.tabPanel = tabPanel;
26905     /**
26906      * The id for this TabPanelItem
26907      * @type String
26908      */
26909     this.id = id;
26910     /** @private */
26911     this.disabled = false;
26912     /** @private */
26913     this.text = text;
26914     /** @private */
26915     this.loaded = false;
26916     this.closable = closable;
26917
26918     /**
26919      * The body element for this TabPanelItem.
26920      * @type Roo.Element
26921      */
26922     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26923     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26924     this.bodyEl.setStyle("display", "block");
26925     this.bodyEl.setStyle("zoom", "1");
26926     this.hideAction();
26927
26928     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26929     /** @private */
26930     this.el = Roo.get(els.el, true);
26931     this.inner = Roo.get(els.inner, true);
26932     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26933     this.pnode = Roo.get(els.el.parentNode, true);
26934     this.el.on("mousedown", this.onTabMouseDown, this);
26935     this.el.on("click", this.onTabClick, this);
26936     /** @private */
26937     if(closable){
26938         var c = Roo.get(els.close, true);
26939         c.dom.title = this.closeText;
26940         c.addClassOnOver("close-over");
26941         c.on("click", this.closeClick, this);
26942      }
26943
26944     this.addEvents({
26945          /**
26946          * @event activate
26947          * Fires when this tab becomes the active tab.
26948          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26949          * @param {Roo.TabPanelItem} this
26950          */
26951         "activate": true,
26952         /**
26953          * @event beforeclose
26954          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26955          * @param {Roo.TabPanelItem} this
26956          * @param {Object} e Set cancel to true on this object to cancel the close.
26957          */
26958         "beforeclose": true,
26959         /**
26960          * @event close
26961          * Fires when this tab is closed.
26962          * @param {Roo.TabPanelItem} this
26963          */
26964          "close": true,
26965         /**
26966          * @event deactivate
26967          * Fires when this tab is no longer the active tab.
26968          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26969          * @param {Roo.TabPanelItem} this
26970          */
26971          "deactivate" : true
26972     });
26973     this.hidden = false;
26974
26975     Roo.TabPanelItem.superclass.constructor.call(this);
26976 };
26977
26978 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26979     purgeListeners : function(){
26980        Roo.util.Observable.prototype.purgeListeners.call(this);
26981        this.el.removeAllListeners();
26982     },
26983     /**
26984      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26985      */
26986     show : function(){
26987         this.pnode.addClass("on");
26988         this.showAction();
26989         if(Roo.isOpera){
26990             this.tabPanel.stripWrap.repaint();
26991         }
26992         this.fireEvent("activate", this.tabPanel, this);
26993     },
26994
26995     /**
26996      * Returns true if this tab is the active tab.
26997      * @return {Boolean}
26998      */
26999     isActive : function(){
27000         return this.tabPanel.getActiveTab() == this;
27001     },
27002
27003     /**
27004      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27005      */
27006     hide : function(){
27007         this.pnode.removeClass("on");
27008         this.hideAction();
27009         this.fireEvent("deactivate", this.tabPanel, this);
27010     },
27011
27012     hideAction : function(){
27013         this.bodyEl.hide();
27014         this.bodyEl.setStyle("position", "absolute");
27015         this.bodyEl.setLeft("-20000px");
27016         this.bodyEl.setTop("-20000px");
27017     },
27018
27019     showAction : function(){
27020         this.bodyEl.setStyle("position", "relative");
27021         this.bodyEl.setTop("");
27022         this.bodyEl.setLeft("");
27023         this.bodyEl.show();
27024     },
27025
27026     /**
27027      * Set the tooltip for the tab.
27028      * @param {String} tooltip The tab's tooltip
27029      */
27030     setTooltip : function(text){
27031         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27032             this.textEl.dom.qtip = text;
27033             this.textEl.dom.removeAttribute('title');
27034         }else{
27035             this.textEl.dom.title = text;
27036         }
27037     },
27038
27039     onTabClick : function(e){
27040         e.preventDefault();
27041         this.tabPanel.activate(this.id);
27042     },
27043
27044     onTabMouseDown : function(e){
27045         e.preventDefault();
27046         this.tabPanel.activate(this.id);
27047     },
27048
27049     getWidth : function(){
27050         return this.inner.getWidth();
27051     },
27052
27053     setWidth : function(width){
27054         var iwidth = width - this.pnode.getPadding("lr");
27055         this.inner.setWidth(iwidth);
27056         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27057         this.pnode.setWidth(width);
27058     },
27059
27060     /**
27061      * Show or hide the tab
27062      * @param {Boolean} hidden True to hide or false to show.
27063      */
27064     setHidden : function(hidden){
27065         this.hidden = hidden;
27066         this.pnode.setStyle("display", hidden ? "none" : "");
27067     },
27068
27069     /**
27070      * Returns true if this tab is "hidden"
27071      * @return {Boolean}
27072      */
27073     isHidden : function(){
27074         return this.hidden;
27075     },
27076
27077     /**
27078      * Returns the text for this tab
27079      * @return {String}
27080      */
27081     getText : function(){
27082         return this.text;
27083     },
27084
27085     autoSize : function(){
27086         //this.el.beginMeasure();
27087         this.textEl.setWidth(1);
27088         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27089         //this.el.endMeasure();
27090     },
27091
27092     /**
27093      * Sets the text for the tab (Note: this also sets the tooltip text)
27094      * @param {String} text The tab's text and tooltip
27095      */
27096     setText : function(text){
27097         this.text = text;
27098         this.textEl.update(text);
27099         this.setTooltip(text);
27100         if(!this.tabPanel.resizeTabs){
27101             this.autoSize();
27102         }
27103     },
27104     /**
27105      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27106      */
27107     activate : function(){
27108         this.tabPanel.activate(this.id);
27109     },
27110
27111     /**
27112      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27113      */
27114     disable : function(){
27115         if(this.tabPanel.active != this){
27116             this.disabled = true;
27117             this.pnode.addClass("disabled");
27118         }
27119     },
27120
27121     /**
27122      * Enables this TabPanelItem if it was previously disabled.
27123      */
27124     enable : function(){
27125         this.disabled = false;
27126         this.pnode.removeClass("disabled");
27127     },
27128
27129     /**
27130      * Sets the content for this TabPanelItem.
27131      * @param {String} content The content
27132      * @param {Boolean} loadScripts true to look for and load scripts
27133      */
27134     setContent : function(content, loadScripts){
27135         this.bodyEl.update(content, loadScripts);
27136     },
27137
27138     /**
27139      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27140      * @return {Roo.UpdateManager} The UpdateManager
27141      */
27142     getUpdateManager : function(){
27143         return this.bodyEl.getUpdateManager();
27144     },
27145
27146     /**
27147      * Set a URL to be used to load the content for this TabPanelItem.
27148      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27149      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
27150      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
27151      * @return {Roo.UpdateManager} The UpdateManager
27152      */
27153     setUrl : function(url, params, loadOnce){
27154         if(this.refreshDelegate){
27155             this.un('activate', this.refreshDelegate);
27156         }
27157         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27158         this.on("activate", this.refreshDelegate);
27159         return this.bodyEl.getUpdateManager();
27160     },
27161
27162     /** @private */
27163     _handleRefresh : function(url, params, loadOnce){
27164         if(!loadOnce || !this.loaded){
27165             var updater = this.bodyEl.getUpdateManager();
27166             updater.update(url, params, this._setLoaded.createDelegate(this));
27167         }
27168     },
27169
27170     /**
27171      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27172      *   Will fail silently if the setUrl method has not been called.
27173      *   This does not activate the panel, just updates its content.
27174      */
27175     refresh : function(){
27176         if(this.refreshDelegate){
27177            this.loaded = false;
27178            this.refreshDelegate();
27179         }
27180     },
27181
27182     /** @private */
27183     _setLoaded : function(){
27184         this.loaded = true;
27185     },
27186
27187     /** @private */
27188     closeClick : function(e){
27189         var o = {};
27190         e.stopEvent();
27191         this.fireEvent("beforeclose", this, o);
27192         if(o.cancel !== true){
27193             this.tabPanel.removeTab(this.id);
27194         }
27195     },
27196     /**
27197      * The text displayed in the tooltip for the close icon.
27198      * @type String
27199      */
27200     closeText : "Close this tab"
27201 });
27202
27203 /** @private */
27204 Roo.TabPanel.prototype.createStrip = function(container){
27205     var strip = document.createElement("div");
27206     strip.className = "x-tabs-wrap";
27207     container.appendChild(strip);
27208     return strip;
27209 };
27210 /** @private */
27211 Roo.TabPanel.prototype.createStripList = function(strip){
27212     // div wrapper for retard IE
27213     // returns the "tr" element.
27214     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27215         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27216         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27217     return strip.firstChild.firstChild.firstChild.firstChild;
27218 };
27219 /** @private */
27220 Roo.TabPanel.prototype.createBody = function(container){
27221     var body = document.createElement("div");
27222     Roo.id(body, "tab-body");
27223     Roo.fly(body).addClass("x-tabs-body");
27224     container.appendChild(body);
27225     return body;
27226 };
27227 /** @private */
27228 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27229     var body = Roo.getDom(id);
27230     if(!body){
27231         body = document.createElement("div");
27232         body.id = id;
27233     }
27234     Roo.fly(body).addClass("x-tabs-item-body");
27235     bodyEl.insertBefore(body, bodyEl.firstChild);
27236     return body;
27237 };
27238 /** @private */
27239 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27240     var td = document.createElement("td");
27241     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27242     //stripEl.appendChild(td);
27243     if(closable){
27244         td.className = "x-tabs-closable";
27245         if(!this.closeTpl){
27246             this.closeTpl = new Roo.Template(
27247                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27248                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27249                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27250             );
27251         }
27252         var el = this.closeTpl.overwrite(td, {"text": text});
27253         var close = el.getElementsByTagName("div")[0];
27254         var inner = el.getElementsByTagName("em")[0];
27255         return {"el": el, "close": close, "inner": inner};
27256     } else {
27257         if(!this.tabTpl){
27258             this.tabTpl = new Roo.Template(
27259                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27260                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27261             );
27262         }
27263         var el = this.tabTpl.overwrite(td, {"text": text});
27264         var inner = el.getElementsByTagName("em")[0];
27265         return {"el": el, "inner": inner};
27266     }
27267 };/*
27268  * Based on:
27269  * Ext JS Library 1.1.1
27270  * Copyright(c) 2006-2007, Ext JS, LLC.
27271  *
27272  * Originally Released Under LGPL - original licence link has changed is not relivant.
27273  *
27274  * Fork - LGPL
27275  * <script type="text/javascript">
27276  */
27277
27278 /**
27279  * @class Roo.Button
27280  * @extends Roo.util.Observable
27281  * Simple Button class
27282  * @cfg {String} text The button text
27283  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27284  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27285  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27286  * @cfg {Object} scope The scope of the handler
27287  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27288  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27289  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27290  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27291  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27292  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27293    applies if enableToggle = true)
27294  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27295  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27296   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27297  * @constructor
27298  * Create a new button
27299  * @param {Object} config The config object
27300  */
27301 Roo.Button = function(renderTo, config)
27302 {
27303     if (!config) {
27304         config = renderTo;
27305         renderTo = config.renderTo || false;
27306     }
27307     
27308     Roo.apply(this, config);
27309     this.addEvents({
27310         /**
27311              * @event click
27312              * Fires when this button is clicked
27313              * @param {Button} this
27314              * @param {EventObject} e The click event
27315              */
27316             "click" : true,
27317         /**
27318              * @event toggle
27319              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27320              * @param {Button} this
27321              * @param {Boolean} pressed
27322              */
27323             "toggle" : true,
27324         /**
27325              * @event mouseover
27326              * Fires when the mouse hovers over the button
27327              * @param {Button} this
27328              * @param {Event} e The event object
27329              */
27330         'mouseover' : true,
27331         /**
27332              * @event mouseout
27333              * Fires when the mouse exits the button
27334              * @param {Button} this
27335              * @param {Event} e The event object
27336              */
27337         'mouseout': true,
27338          /**
27339              * @event render
27340              * Fires when the button is rendered
27341              * @param {Button} this
27342              */
27343         'render': true
27344     });
27345     if(this.menu){
27346         this.menu = Roo.menu.MenuMgr.get(this.menu);
27347     }
27348     // register listeners first!!  - so render can be captured..
27349     Roo.util.Observable.call(this);
27350     if(renderTo){
27351         this.render(renderTo);
27352     }
27353     
27354   
27355 };
27356
27357 Roo.extend(Roo.Button, Roo.util.Observable, {
27358     /**
27359      * 
27360      */
27361     
27362     /**
27363      * Read-only. True if this button is hidden
27364      * @type Boolean
27365      */
27366     hidden : false,
27367     /**
27368      * Read-only. True if this button is disabled
27369      * @type Boolean
27370      */
27371     disabled : false,
27372     /**
27373      * Read-only. True if this button is pressed (only if enableToggle = true)
27374      * @type Boolean
27375      */
27376     pressed : false,
27377
27378     /**
27379      * @cfg {Number} tabIndex 
27380      * The DOM tabIndex for this button (defaults to undefined)
27381      */
27382     tabIndex : undefined,
27383
27384     /**
27385      * @cfg {Boolean} enableToggle
27386      * True to enable pressed/not pressed toggling (defaults to false)
27387      */
27388     enableToggle: false,
27389     /**
27390      * @cfg {Mixed} menu
27391      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27392      */
27393     menu : undefined,
27394     /**
27395      * @cfg {String} menuAlign
27396      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27397      */
27398     menuAlign : "tl-bl?",
27399
27400     /**
27401      * @cfg {String} iconCls
27402      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27403      */
27404     iconCls : undefined,
27405     /**
27406      * @cfg {String} type
27407      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27408      */
27409     type : 'button',
27410
27411     // private
27412     menuClassTarget: 'tr',
27413
27414     /**
27415      * @cfg {String} clickEvent
27416      * The type of event to map to the button's event handler (defaults to 'click')
27417      */
27418     clickEvent : 'click',
27419
27420     /**
27421      * @cfg {Boolean} handleMouseEvents
27422      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27423      */
27424     handleMouseEvents : true,
27425
27426     /**
27427      * @cfg {String} tooltipType
27428      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27429      */
27430     tooltipType : 'qtip',
27431
27432     /**
27433      * @cfg {String} cls
27434      * A CSS class to apply to the button's main element.
27435      */
27436     
27437     /**
27438      * @cfg {Roo.Template} template (Optional)
27439      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27440      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27441      * require code modifications if required elements (e.g. a button) aren't present.
27442      */
27443
27444     // private
27445     render : function(renderTo){
27446         var btn;
27447         if(this.hideParent){
27448             this.parentEl = Roo.get(renderTo);
27449         }
27450         if(!this.dhconfig){
27451             if(!this.template){
27452                 if(!Roo.Button.buttonTemplate){
27453                     // hideous table template
27454                     Roo.Button.buttonTemplate = new Roo.Template(
27455                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27456                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
27457                         "</tr></tbody></table>");
27458                 }
27459                 this.template = Roo.Button.buttonTemplate;
27460             }
27461             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27462             var btnEl = btn.child("button:first");
27463             btnEl.on('focus', this.onFocus, this);
27464             btnEl.on('blur', this.onBlur, this);
27465             if(this.cls){
27466                 btn.addClass(this.cls);
27467             }
27468             if(this.icon){
27469                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27470             }
27471             if(this.iconCls){
27472                 btnEl.addClass(this.iconCls);
27473                 if(!this.cls){
27474                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27475                 }
27476             }
27477             if(this.tabIndex !== undefined){
27478                 btnEl.dom.tabIndex = this.tabIndex;
27479             }
27480             if(this.tooltip){
27481                 if(typeof this.tooltip == 'object'){
27482                     Roo.QuickTips.tips(Roo.apply({
27483                           target: btnEl.id
27484                     }, this.tooltip));
27485                 } else {
27486                     btnEl.dom[this.tooltipType] = this.tooltip;
27487                 }
27488             }
27489         }else{
27490             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27491         }
27492         this.el = btn;
27493         if(this.id){
27494             this.el.dom.id = this.el.id = this.id;
27495         }
27496         if(this.menu){
27497             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27498             this.menu.on("show", this.onMenuShow, this);
27499             this.menu.on("hide", this.onMenuHide, this);
27500         }
27501         btn.addClass("x-btn");
27502         if(Roo.isIE && !Roo.isIE7){
27503             this.autoWidth.defer(1, this);
27504         }else{
27505             this.autoWidth();
27506         }
27507         if(this.handleMouseEvents){
27508             btn.on("mouseover", this.onMouseOver, this);
27509             btn.on("mouseout", this.onMouseOut, this);
27510             btn.on("mousedown", this.onMouseDown, this);
27511         }
27512         btn.on(this.clickEvent, this.onClick, this);
27513         //btn.on("mouseup", this.onMouseUp, this);
27514         if(this.hidden){
27515             this.hide();
27516         }
27517         if(this.disabled){
27518             this.disable();
27519         }
27520         Roo.ButtonToggleMgr.register(this);
27521         if(this.pressed){
27522             this.el.addClass("x-btn-pressed");
27523         }
27524         if(this.repeat){
27525             var repeater = new Roo.util.ClickRepeater(btn,
27526                 typeof this.repeat == "object" ? this.repeat : {}
27527             );
27528             repeater.on("click", this.onClick,  this);
27529         }
27530         
27531         this.fireEvent('render', this);
27532         
27533     },
27534     /**
27535      * Returns the button's underlying element
27536      * @return {Roo.Element} The element
27537      */
27538     getEl : function(){
27539         return this.el;  
27540     },
27541     
27542     /**
27543      * Destroys this Button and removes any listeners.
27544      */
27545     destroy : function(){
27546         Roo.ButtonToggleMgr.unregister(this);
27547         this.el.removeAllListeners();
27548         this.purgeListeners();
27549         this.el.remove();
27550     },
27551
27552     // private
27553     autoWidth : function(){
27554         if(this.el){
27555             this.el.setWidth("auto");
27556             if(Roo.isIE7 && Roo.isStrict){
27557                 var ib = this.el.child('button');
27558                 if(ib && ib.getWidth() > 20){
27559                     ib.clip();
27560                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27561                 }
27562             }
27563             if(this.minWidth){
27564                 if(this.hidden){
27565                     this.el.beginMeasure();
27566                 }
27567                 if(this.el.getWidth() < this.minWidth){
27568                     this.el.setWidth(this.minWidth);
27569                 }
27570                 if(this.hidden){
27571                     this.el.endMeasure();
27572                 }
27573             }
27574         }
27575     },
27576
27577     /**
27578      * Assigns this button's click handler
27579      * @param {Function} handler The function to call when the button is clicked
27580      * @param {Object} scope (optional) Scope for the function passed in
27581      */
27582     setHandler : function(handler, scope){
27583         this.handler = handler;
27584         this.scope = scope;  
27585     },
27586     
27587     /**
27588      * Sets this button's text
27589      * @param {String} text The button text
27590      */
27591     setText : function(text){
27592         this.text = text;
27593         if(this.el){
27594             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27595         }
27596         this.autoWidth();
27597     },
27598     
27599     /**
27600      * Gets the text for this button
27601      * @return {String} The button text
27602      */
27603     getText : function(){
27604         return this.text;  
27605     },
27606     
27607     /**
27608      * Show this button
27609      */
27610     show: function(){
27611         this.hidden = false;
27612         if(this.el){
27613             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27614         }
27615     },
27616     
27617     /**
27618      * Hide this button
27619      */
27620     hide: function(){
27621         this.hidden = true;
27622         if(this.el){
27623             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27624         }
27625     },
27626     
27627     /**
27628      * Convenience function for boolean show/hide
27629      * @param {Boolean} visible True to show, false to hide
27630      */
27631     setVisible: function(visible){
27632         if(visible) {
27633             this.show();
27634         }else{
27635             this.hide();
27636         }
27637     },
27638     
27639     /**
27640      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27641      * @param {Boolean} state (optional) Force a particular state
27642      */
27643     toggle : function(state){
27644         state = state === undefined ? !this.pressed : state;
27645         if(state != this.pressed){
27646             if(state){
27647                 this.el.addClass("x-btn-pressed");
27648                 this.pressed = true;
27649                 this.fireEvent("toggle", this, true);
27650             }else{
27651                 this.el.removeClass("x-btn-pressed");
27652                 this.pressed = false;
27653                 this.fireEvent("toggle", this, false);
27654             }
27655             if(this.toggleHandler){
27656                 this.toggleHandler.call(this.scope || this, this, state);
27657             }
27658         }
27659     },
27660     
27661     /**
27662      * Focus the button
27663      */
27664     focus : function(){
27665         this.el.child('button:first').focus();
27666     },
27667     
27668     /**
27669      * Disable this button
27670      */
27671     disable : function(){
27672         if(this.el){
27673             this.el.addClass("x-btn-disabled");
27674         }
27675         this.disabled = true;
27676     },
27677     
27678     /**
27679      * Enable this button
27680      */
27681     enable : function(){
27682         if(this.el){
27683             this.el.removeClass("x-btn-disabled");
27684         }
27685         this.disabled = false;
27686     },
27687
27688     /**
27689      * Convenience function for boolean enable/disable
27690      * @param {Boolean} enabled True to enable, false to disable
27691      */
27692     setDisabled : function(v){
27693         this[v !== true ? "enable" : "disable"]();
27694     },
27695
27696     // private
27697     onClick : function(e){
27698         if(e){
27699             e.preventDefault();
27700         }
27701         if(e.button != 0){
27702             return;
27703         }
27704         if(!this.disabled){
27705             if(this.enableToggle){
27706                 this.toggle();
27707             }
27708             if(this.menu && !this.menu.isVisible()){
27709                 this.menu.show(this.el, this.menuAlign);
27710             }
27711             this.fireEvent("click", this, e);
27712             if(this.handler){
27713                 this.el.removeClass("x-btn-over");
27714                 this.handler.call(this.scope || this, this, e);
27715             }
27716         }
27717     },
27718     // private
27719     onMouseOver : function(e){
27720         if(!this.disabled){
27721             this.el.addClass("x-btn-over");
27722             this.fireEvent('mouseover', this, e);
27723         }
27724     },
27725     // private
27726     onMouseOut : function(e){
27727         if(!e.within(this.el,  true)){
27728             this.el.removeClass("x-btn-over");
27729             this.fireEvent('mouseout', this, e);
27730         }
27731     },
27732     // private
27733     onFocus : function(e){
27734         if(!this.disabled){
27735             this.el.addClass("x-btn-focus");
27736         }
27737     },
27738     // private
27739     onBlur : function(e){
27740         this.el.removeClass("x-btn-focus");
27741     },
27742     // private
27743     onMouseDown : function(e){
27744         if(!this.disabled && e.button == 0){
27745             this.el.addClass("x-btn-click");
27746             Roo.get(document).on('mouseup', this.onMouseUp, this);
27747         }
27748     },
27749     // private
27750     onMouseUp : function(e){
27751         if(e.button == 0){
27752             this.el.removeClass("x-btn-click");
27753             Roo.get(document).un('mouseup', this.onMouseUp, this);
27754         }
27755     },
27756     // private
27757     onMenuShow : function(e){
27758         this.el.addClass("x-btn-menu-active");
27759     },
27760     // private
27761     onMenuHide : function(e){
27762         this.el.removeClass("x-btn-menu-active");
27763     }   
27764 });
27765
27766 // Private utility class used by Button
27767 Roo.ButtonToggleMgr = function(){
27768    var groups = {};
27769    
27770    function toggleGroup(btn, state){
27771        if(state){
27772            var g = groups[btn.toggleGroup];
27773            for(var i = 0, l = g.length; i < l; i++){
27774                if(g[i] != btn){
27775                    g[i].toggle(false);
27776                }
27777            }
27778        }
27779    }
27780    
27781    return {
27782        register : function(btn){
27783            if(!btn.toggleGroup){
27784                return;
27785            }
27786            var g = groups[btn.toggleGroup];
27787            if(!g){
27788                g = groups[btn.toggleGroup] = [];
27789            }
27790            g.push(btn);
27791            btn.on("toggle", toggleGroup);
27792        },
27793        
27794        unregister : function(btn){
27795            if(!btn.toggleGroup){
27796                return;
27797            }
27798            var g = groups[btn.toggleGroup];
27799            if(g){
27800                g.remove(btn);
27801                btn.un("toggle", toggleGroup);
27802            }
27803        }
27804    };
27805 }();/*
27806  * Based on:
27807  * Ext JS Library 1.1.1
27808  * Copyright(c) 2006-2007, Ext JS, LLC.
27809  *
27810  * Originally Released Under LGPL - original licence link has changed is not relivant.
27811  *
27812  * Fork - LGPL
27813  * <script type="text/javascript">
27814  */
27815  
27816 /**
27817  * @class Roo.SplitButton
27818  * @extends Roo.Button
27819  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27820  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27821  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27822  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27823  * @cfg {String} arrowTooltip The title attribute of the arrow
27824  * @constructor
27825  * Create a new menu button
27826  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27827  * @param {Object} config The config object
27828  */
27829 Roo.SplitButton = function(renderTo, config){
27830     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27831     /**
27832      * @event arrowclick
27833      * Fires when this button's arrow is clicked
27834      * @param {SplitButton} this
27835      * @param {EventObject} e The click event
27836      */
27837     this.addEvents({"arrowclick":true});
27838 };
27839
27840 Roo.extend(Roo.SplitButton, Roo.Button, {
27841     render : function(renderTo){
27842         // this is one sweet looking template!
27843         var tpl = new Roo.Template(
27844             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27845             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27846             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
27847             "</tbody></table></td><td>",
27848             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27849             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
27850             "</tbody></table></td></tr></table>"
27851         );
27852         var btn = tpl.append(renderTo, [this.text, this.type], true);
27853         var btnEl = btn.child("button");
27854         if(this.cls){
27855             btn.addClass(this.cls);
27856         }
27857         if(this.icon){
27858             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27859         }
27860         if(this.iconCls){
27861             btnEl.addClass(this.iconCls);
27862             if(!this.cls){
27863                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27864             }
27865         }
27866         this.el = btn;
27867         if(this.handleMouseEvents){
27868             btn.on("mouseover", this.onMouseOver, this);
27869             btn.on("mouseout", this.onMouseOut, this);
27870             btn.on("mousedown", this.onMouseDown, this);
27871             btn.on("mouseup", this.onMouseUp, this);
27872         }
27873         btn.on(this.clickEvent, this.onClick, this);
27874         if(this.tooltip){
27875             if(typeof this.tooltip == 'object'){
27876                 Roo.QuickTips.tips(Roo.apply({
27877                       target: btnEl.id
27878                 }, this.tooltip));
27879             } else {
27880                 btnEl.dom[this.tooltipType] = this.tooltip;
27881             }
27882         }
27883         if(this.arrowTooltip){
27884             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27885         }
27886         if(this.hidden){
27887             this.hide();
27888         }
27889         if(this.disabled){
27890             this.disable();
27891         }
27892         if(this.pressed){
27893             this.el.addClass("x-btn-pressed");
27894         }
27895         if(Roo.isIE && !Roo.isIE7){
27896             this.autoWidth.defer(1, this);
27897         }else{
27898             this.autoWidth();
27899         }
27900         if(this.menu){
27901             this.menu.on("show", this.onMenuShow, this);
27902             this.menu.on("hide", this.onMenuHide, this);
27903         }
27904         this.fireEvent('render', this);
27905     },
27906
27907     // private
27908     autoWidth : function(){
27909         if(this.el){
27910             var tbl = this.el.child("table:first");
27911             var tbl2 = this.el.child("table:last");
27912             this.el.setWidth("auto");
27913             tbl.setWidth("auto");
27914             if(Roo.isIE7 && Roo.isStrict){
27915                 var ib = this.el.child('button:first');
27916                 if(ib && ib.getWidth() > 20){
27917                     ib.clip();
27918                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27919                 }
27920             }
27921             if(this.minWidth){
27922                 if(this.hidden){
27923                     this.el.beginMeasure();
27924                 }
27925                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27926                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27927                 }
27928                 if(this.hidden){
27929                     this.el.endMeasure();
27930                 }
27931             }
27932             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27933         } 
27934     },
27935     /**
27936      * Sets this button's click handler
27937      * @param {Function} handler The function to call when the button is clicked
27938      * @param {Object} scope (optional) Scope for the function passed above
27939      */
27940     setHandler : function(handler, scope){
27941         this.handler = handler;
27942         this.scope = scope;  
27943     },
27944     
27945     /**
27946      * Sets this button's arrow click handler
27947      * @param {Function} handler The function to call when the arrow is clicked
27948      * @param {Object} scope (optional) Scope for the function passed above
27949      */
27950     setArrowHandler : function(handler, scope){
27951         this.arrowHandler = handler;
27952         this.scope = scope;  
27953     },
27954     
27955     /**
27956      * Focus the button
27957      */
27958     focus : function(){
27959         if(this.el){
27960             this.el.child("button:first").focus();
27961         }
27962     },
27963
27964     // private
27965     onClick : function(e){
27966         e.preventDefault();
27967         if(!this.disabled){
27968             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27969                 if(this.menu && !this.menu.isVisible()){
27970                     this.menu.show(this.el, this.menuAlign);
27971                 }
27972                 this.fireEvent("arrowclick", this, e);
27973                 if(this.arrowHandler){
27974                     this.arrowHandler.call(this.scope || this, this, e);
27975                 }
27976             }else{
27977                 this.fireEvent("click", this, e);
27978                 if(this.handler){
27979                     this.handler.call(this.scope || this, this, e);
27980                 }
27981             }
27982         }
27983     },
27984     // private
27985     onMouseDown : function(e){
27986         if(!this.disabled){
27987             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27988         }
27989     },
27990     // private
27991     onMouseUp : function(e){
27992         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27993     }   
27994 });
27995
27996
27997 // backwards compat
27998 Roo.MenuButton = Roo.SplitButton;/*
27999  * Based on:
28000  * Ext JS Library 1.1.1
28001  * Copyright(c) 2006-2007, Ext JS, LLC.
28002  *
28003  * Originally Released Under LGPL - original licence link has changed is not relivant.
28004  *
28005  * Fork - LGPL
28006  * <script type="text/javascript">
28007  */
28008
28009 /**
28010  * @class Roo.Toolbar
28011  * Basic Toolbar class.
28012  * @constructor
28013  * Creates a new Toolbar
28014  * @param {Object} container The config object
28015  */ 
28016 Roo.Toolbar = function(container, buttons, config)
28017 {
28018     /// old consturctor format still supported..
28019     if(container instanceof Array){ // omit the container for later rendering
28020         buttons = container;
28021         config = buttons;
28022         container = null;
28023     }
28024     if (typeof(container) == 'object' && container.xtype) {
28025         config = container;
28026         container = config.container;
28027         buttons = config.buttons || []; // not really - use items!!
28028     }
28029     var xitems = [];
28030     if (config && config.items) {
28031         xitems = config.items;
28032         delete config.items;
28033     }
28034     Roo.apply(this, config);
28035     this.buttons = buttons;
28036     
28037     if(container){
28038         this.render(container);
28039     }
28040     this.xitems = xitems;
28041     Roo.each(xitems, function(b) {
28042         this.add(b);
28043     }, this);
28044     
28045 };
28046
28047 Roo.Toolbar.prototype = {
28048     /**
28049      * @cfg {Array} items
28050      * array of button configs or elements to add (will be converted to a MixedCollection)
28051      */
28052     
28053     /**
28054      * @cfg {String/HTMLElement/Element} container
28055      * The id or element that will contain the toolbar
28056      */
28057     // private
28058     render : function(ct){
28059         this.el = Roo.get(ct);
28060         if(this.cls){
28061             this.el.addClass(this.cls);
28062         }
28063         // using a table allows for vertical alignment
28064         // 100% width is needed by Safari...
28065         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28066         this.tr = this.el.child("tr", true);
28067         var autoId = 0;
28068         this.items = new Roo.util.MixedCollection(false, function(o){
28069             return o.id || ("item" + (++autoId));
28070         });
28071         if(this.buttons){
28072             this.add.apply(this, this.buttons);
28073             delete this.buttons;
28074         }
28075     },
28076
28077     /**
28078      * Adds element(s) to the toolbar -- this function takes a variable number of 
28079      * arguments of mixed type and adds them to the toolbar.
28080      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28081      * <ul>
28082      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28083      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28084      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28085      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28086      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28087      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28088      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28089      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28090      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28091      * </ul>
28092      * @param {Mixed} arg2
28093      * @param {Mixed} etc.
28094      */
28095     add : function(){
28096         var a = arguments, l = a.length;
28097         for(var i = 0; i < l; i++){
28098             this._add(a[i]);
28099         }
28100     },
28101     // private..
28102     _add : function(el) {
28103         
28104         if (el.xtype) {
28105             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28106         }
28107         
28108         if (el.applyTo){ // some kind of form field
28109             return this.addField(el);
28110         } 
28111         if (el.render){ // some kind of Toolbar.Item
28112             return this.addItem(el);
28113         }
28114         if (typeof el == "string"){ // string
28115             if(el == "separator" || el == "-"){
28116                 return this.addSeparator();
28117             }
28118             if (el == " "){
28119                 return this.addSpacer();
28120             }
28121             if(el == "->"){
28122                 return this.addFill();
28123             }
28124             return this.addText(el);
28125             
28126         }
28127         if(el.tagName){ // element
28128             return this.addElement(el);
28129         }
28130         if(typeof el == "object"){ // must be button config?
28131             return this.addButton(el);
28132         }
28133         // and now what?!?!
28134         return false;
28135         
28136     },
28137     
28138     /**
28139      * Add an Xtype element
28140      * @param {Object} xtype Xtype Object
28141      * @return {Object} created Object
28142      */
28143     addxtype : function(e){
28144         return this.add(e);  
28145     },
28146     
28147     /**
28148      * Returns the Element for this toolbar.
28149      * @return {Roo.Element}
28150      */
28151     getEl : function(){
28152         return this.el;  
28153     },
28154     
28155     /**
28156      * Adds a separator
28157      * @return {Roo.Toolbar.Item} The separator item
28158      */
28159     addSeparator : function(){
28160         return this.addItem(new Roo.Toolbar.Separator());
28161     },
28162
28163     /**
28164      * Adds a spacer element
28165      * @return {Roo.Toolbar.Spacer} The spacer item
28166      */
28167     addSpacer : function(){
28168         return this.addItem(new Roo.Toolbar.Spacer());
28169     },
28170
28171     /**
28172      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28173      * @return {Roo.Toolbar.Fill} The fill item
28174      */
28175     addFill : function(){
28176         return this.addItem(new Roo.Toolbar.Fill());
28177     },
28178
28179     /**
28180      * Adds any standard HTML element to the toolbar
28181      * @param {String/HTMLElement/Element} el The element or id of the element to add
28182      * @return {Roo.Toolbar.Item} The element's item
28183      */
28184     addElement : function(el){
28185         return this.addItem(new Roo.Toolbar.Item(el));
28186     },
28187     /**
28188      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28189      * @type Roo.util.MixedCollection  
28190      */
28191     items : false,
28192      
28193     /**
28194      * Adds any Toolbar.Item or subclass
28195      * @param {Roo.Toolbar.Item} item
28196      * @return {Roo.Toolbar.Item} The item
28197      */
28198     addItem : function(item){
28199         var td = this.nextBlock();
28200         item.render(td);
28201         this.items.add(item);
28202         return item;
28203     },
28204     
28205     /**
28206      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28207      * @param {Object/Array} config A button config or array of configs
28208      * @return {Roo.Toolbar.Button/Array}
28209      */
28210     addButton : function(config){
28211         if(config instanceof Array){
28212             var buttons = [];
28213             for(var i = 0, len = config.length; i < len; i++) {
28214                 buttons.push(this.addButton(config[i]));
28215             }
28216             return buttons;
28217         }
28218         var b = config;
28219         if(!(config instanceof Roo.Toolbar.Button)){
28220             b = config.split ?
28221                 new Roo.Toolbar.SplitButton(config) :
28222                 new Roo.Toolbar.Button(config);
28223         }
28224         var td = this.nextBlock();
28225         b.render(td);
28226         this.items.add(b);
28227         return b;
28228     },
28229     
28230     /**
28231      * Adds text to the toolbar
28232      * @param {String} text The text to add
28233      * @return {Roo.Toolbar.Item} The element's item
28234      */
28235     addText : function(text){
28236         return this.addItem(new Roo.Toolbar.TextItem(text));
28237     },
28238     
28239     /**
28240      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28241      * @param {Number} index The index where the item is to be inserted
28242      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28243      * @return {Roo.Toolbar.Button/Item}
28244      */
28245     insertButton : function(index, item){
28246         if(item instanceof Array){
28247             var buttons = [];
28248             for(var i = 0, len = item.length; i < len; i++) {
28249                buttons.push(this.insertButton(index + i, item[i]));
28250             }
28251             return buttons;
28252         }
28253         if (!(item instanceof Roo.Toolbar.Button)){
28254            item = new Roo.Toolbar.Button(item);
28255         }
28256         var td = document.createElement("td");
28257         this.tr.insertBefore(td, this.tr.childNodes[index]);
28258         item.render(td);
28259         this.items.insert(index, item);
28260         return item;
28261     },
28262     
28263     /**
28264      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28265      * @param {Object} config
28266      * @return {Roo.Toolbar.Item} The element's item
28267      */
28268     addDom : function(config, returnEl){
28269         var td = this.nextBlock();
28270         Roo.DomHelper.overwrite(td, config);
28271         var ti = new Roo.Toolbar.Item(td.firstChild);
28272         ti.render(td);
28273         this.items.add(ti);
28274         return ti;
28275     },
28276
28277     /**
28278      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28279      * @type Roo.util.MixedCollection  
28280      */
28281     fields : false,
28282     
28283     /**
28284      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28285      * Note: the field should not have been rendered yet. For a field that has already been
28286      * rendered, use {@link #addElement}.
28287      * @param {Roo.form.Field} field
28288      * @return {Roo.ToolbarItem}
28289      */
28290      
28291       
28292     addField : function(field) {
28293         if (!this.fields) {
28294             var autoId = 0;
28295             this.fields = new Roo.util.MixedCollection(false, function(o){
28296                 return o.id || ("item" + (++autoId));
28297             });
28298
28299         }
28300         
28301         var td = this.nextBlock();
28302         field.render(td);
28303         var ti = new Roo.Toolbar.Item(td.firstChild);
28304         ti.render(td);
28305         this.items.add(ti);
28306         this.fields.add(field);
28307         return ti;
28308     },
28309     /**
28310      * Hide the toolbar
28311      * @method hide
28312      */
28313      
28314       
28315     hide : function()
28316     {
28317         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28318         this.el.child('div').hide();
28319     },
28320     /**
28321      * Show the toolbar
28322      * @method show
28323      */
28324     show : function()
28325     {
28326         this.el.child('div').show();
28327     },
28328       
28329     // private
28330     nextBlock : function(){
28331         var td = document.createElement("td");
28332         this.tr.appendChild(td);
28333         return td;
28334     },
28335
28336     // private
28337     destroy : function(){
28338         if(this.items){ // rendered?
28339             Roo.destroy.apply(Roo, this.items.items);
28340         }
28341         if(this.fields){ // rendered?
28342             Roo.destroy.apply(Roo, this.fields.items);
28343         }
28344         Roo.Element.uncache(this.el, this.tr);
28345     }
28346 };
28347
28348 /**
28349  * @class Roo.Toolbar.Item
28350  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28351  * @constructor
28352  * Creates a new Item
28353  * @param {HTMLElement} el 
28354  */
28355 Roo.Toolbar.Item = function(el){
28356     this.el = Roo.getDom(el);
28357     this.id = Roo.id(this.el);
28358     this.hidden = false;
28359 };
28360
28361 Roo.Toolbar.Item.prototype = {
28362     
28363     /**
28364      * Get this item's HTML Element
28365      * @return {HTMLElement}
28366      */
28367     getEl : function(){
28368        return this.el;  
28369     },
28370
28371     // private
28372     render : function(td){
28373         this.td = td;
28374         td.appendChild(this.el);
28375     },
28376     
28377     /**
28378      * Removes and destroys this item.
28379      */
28380     destroy : function(){
28381         this.td.parentNode.removeChild(this.td);
28382     },
28383     
28384     /**
28385      * Shows this item.
28386      */
28387     show: function(){
28388         this.hidden = false;
28389         this.td.style.display = "";
28390     },
28391     
28392     /**
28393      * Hides this item.
28394      */
28395     hide: function(){
28396         this.hidden = true;
28397         this.td.style.display = "none";
28398     },
28399     
28400     /**
28401      * Convenience function for boolean show/hide.
28402      * @param {Boolean} visible true to show/false to hide
28403      */
28404     setVisible: function(visible){
28405         if(visible) {
28406             this.show();
28407         }else{
28408             this.hide();
28409         }
28410     },
28411     
28412     /**
28413      * Try to focus this item.
28414      */
28415     focus : function(){
28416         Roo.fly(this.el).focus();
28417     },
28418     
28419     /**
28420      * Disables this item.
28421      */
28422     disable : function(){
28423         Roo.fly(this.td).addClass("x-item-disabled");
28424         this.disabled = true;
28425         this.el.disabled = true;
28426     },
28427     
28428     /**
28429      * Enables this item.
28430      */
28431     enable : function(){
28432         Roo.fly(this.td).removeClass("x-item-disabled");
28433         this.disabled = false;
28434         this.el.disabled = false;
28435     }
28436 };
28437
28438
28439 /**
28440  * @class Roo.Toolbar.Separator
28441  * @extends Roo.Toolbar.Item
28442  * A simple toolbar separator class
28443  * @constructor
28444  * Creates a new Separator
28445  */
28446 Roo.Toolbar.Separator = function(){
28447     var s = document.createElement("span");
28448     s.className = "ytb-sep";
28449     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28450 };
28451 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28452     enable:Roo.emptyFn,
28453     disable:Roo.emptyFn,
28454     focus:Roo.emptyFn
28455 });
28456
28457 /**
28458  * @class Roo.Toolbar.Spacer
28459  * @extends Roo.Toolbar.Item
28460  * A simple element that adds extra horizontal space to a toolbar.
28461  * @constructor
28462  * Creates a new Spacer
28463  */
28464 Roo.Toolbar.Spacer = function(){
28465     var s = document.createElement("div");
28466     s.className = "ytb-spacer";
28467     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28468 };
28469 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28470     enable:Roo.emptyFn,
28471     disable:Roo.emptyFn,
28472     focus:Roo.emptyFn
28473 });
28474
28475 /**
28476  * @class Roo.Toolbar.Fill
28477  * @extends Roo.Toolbar.Spacer
28478  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28479  * @constructor
28480  * Creates a new Spacer
28481  */
28482 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28483     // private
28484     render : function(td){
28485         td.style.width = '100%';
28486         Roo.Toolbar.Fill.superclass.render.call(this, td);
28487     }
28488 });
28489
28490 /**
28491  * @class Roo.Toolbar.TextItem
28492  * @extends Roo.Toolbar.Item
28493  * A simple class that renders text directly into a toolbar.
28494  * @constructor
28495  * Creates a new TextItem
28496  * @param {String} text
28497  */
28498 Roo.Toolbar.TextItem = function(text){
28499     if (typeof(text) == 'object') {
28500         text = text.text;
28501     }
28502     var s = document.createElement("span");
28503     s.className = "ytb-text";
28504     s.innerHTML = text;
28505     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28506 };
28507 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28508     enable:Roo.emptyFn,
28509     disable:Roo.emptyFn,
28510     focus:Roo.emptyFn
28511 });
28512
28513 /**
28514  * @class Roo.Toolbar.Button
28515  * @extends Roo.Button
28516  * A button that renders into a toolbar.
28517  * @constructor
28518  * Creates a new Button
28519  * @param {Object} config A standard {@link Roo.Button} config object
28520  */
28521 Roo.Toolbar.Button = function(config){
28522     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28523 };
28524 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28525     render : function(td){
28526         this.td = td;
28527         Roo.Toolbar.Button.superclass.render.call(this, td);
28528     },
28529     
28530     /**
28531      * Removes and destroys this button
28532      */
28533     destroy : function(){
28534         Roo.Toolbar.Button.superclass.destroy.call(this);
28535         this.td.parentNode.removeChild(this.td);
28536     },
28537     
28538     /**
28539      * Shows this button
28540      */
28541     show: function(){
28542         this.hidden = false;
28543         this.td.style.display = "";
28544     },
28545     
28546     /**
28547      * Hides this button
28548      */
28549     hide: function(){
28550         this.hidden = true;
28551         this.td.style.display = "none";
28552     },
28553
28554     /**
28555      * Disables this item
28556      */
28557     disable : function(){
28558         Roo.fly(this.td).addClass("x-item-disabled");
28559         this.disabled = true;
28560     },
28561
28562     /**
28563      * Enables this item
28564      */
28565     enable : function(){
28566         Roo.fly(this.td).removeClass("x-item-disabled");
28567         this.disabled = false;
28568     }
28569 });
28570 // backwards compat
28571 Roo.ToolbarButton = Roo.Toolbar.Button;
28572
28573 /**
28574  * @class Roo.Toolbar.SplitButton
28575  * @extends Roo.SplitButton
28576  * A menu button that renders into a toolbar.
28577  * @constructor
28578  * Creates a new SplitButton
28579  * @param {Object} config A standard {@link Roo.SplitButton} config object
28580  */
28581 Roo.Toolbar.SplitButton = function(config){
28582     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28583 };
28584 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28585     render : function(td){
28586         this.td = td;
28587         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28588     },
28589     
28590     /**
28591      * Removes and destroys this button
28592      */
28593     destroy : function(){
28594         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28595         this.td.parentNode.removeChild(this.td);
28596     },
28597     
28598     /**
28599      * Shows this button
28600      */
28601     show: function(){
28602         this.hidden = false;
28603         this.td.style.display = "";
28604     },
28605     
28606     /**
28607      * Hides this button
28608      */
28609     hide: function(){
28610         this.hidden = true;
28611         this.td.style.display = "none";
28612     }
28613 });
28614
28615 // backwards compat
28616 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28617  * Based on:
28618  * Ext JS Library 1.1.1
28619  * Copyright(c) 2006-2007, Ext JS, LLC.
28620  *
28621  * Originally Released Under LGPL - original licence link has changed is not relivant.
28622  *
28623  * Fork - LGPL
28624  * <script type="text/javascript">
28625  */
28626  
28627 /**
28628  * @class Roo.PagingToolbar
28629  * @extends Roo.Toolbar
28630  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28631  * @constructor
28632  * Create a new PagingToolbar
28633  * @param {Object} config The config object
28634  */
28635 Roo.PagingToolbar = function(el, ds, config)
28636 {
28637     // old args format still supported... - xtype is prefered..
28638     if (typeof(el) == 'object' && el.xtype) {
28639         // created from xtype...
28640         config = el;
28641         ds = el.dataSource;
28642         el = config.container;
28643     }
28644     var items = [];
28645     if (config.items) {
28646         items = config.items;
28647         config.items = [];
28648     }
28649     
28650     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28651     this.ds = ds;
28652     this.cursor = 0;
28653     this.renderButtons(this.el);
28654     this.bind(ds);
28655     
28656     // supprot items array.
28657    
28658     Roo.each(items, function(e) {
28659         this.add(Roo.factory(e));
28660     },this);
28661     
28662 };
28663
28664 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28665     /**
28666      * @cfg {Roo.data.Store} dataSource
28667      * The underlying data store providing the paged data
28668      */
28669     /**
28670      * @cfg {String/HTMLElement/Element} container
28671      * container The id or element that will contain the toolbar
28672      */
28673     /**
28674      * @cfg {Boolean} displayInfo
28675      * True to display the displayMsg (defaults to false)
28676      */
28677     /**
28678      * @cfg {Number} pageSize
28679      * The number of records to display per page (defaults to 20)
28680      */
28681     pageSize: 20,
28682     /**
28683      * @cfg {String} displayMsg
28684      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28685      */
28686     displayMsg : 'Displaying {0} - {1} of {2}',
28687     /**
28688      * @cfg {String} emptyMsg
28689      * The message to display when no records are found (defaults to "No data to display")
28690      */
28691     emptyMsg : 'No data to display',
28692     /**
28693      * Customizable piece of the default paging text (defaults to "Page")
28694      * @type String
28695      */
28696     beforePageText : "Page",
28697     /**
28698      * Customizable piece of the default paging text (defaults to "of %0")
28699      * @type String
28700      */
28701     afterPageText : "of {0}",
28702     /**
28703      * Customizable piece of the default paging text (defaults to "First Page")
28704      * @type String
28705      */
28706     firstText : "First Page",
28707     /**
28708      * Customizable piece of the default paging text (defaults to "Previous Page")
28709      * @type String
28710      */
28711     prevText : "Previous Page",
28712     /**
28713      * Customizable piece of the default paging text (defaults to "Next Page")
28714      * @type String
28715      */
28716     nextText : "Next Page",
28717     /**
28718      * Customizable piece of the default paging text (defaults to "Last Page")
28719      * @type String
28720      */
28721     lastText : "Last Page",
28722     /**
28723      * Customizable piece of the default paging text (defaults to "Refresh")
28724      * @type String
28725      */
28726     refreshText : "Refresh",
28727
28728     // private
28729     renderButtons : function(el){
28730         Roo.PagingToolbar.superclass.render.call(this, el);
28731         this.first = this.addButton({
28732             tooltip: this.firstText,
28733             cls: "x-btn-icon x-grid-page-first",
28734             disabled: true,
28735             handler: this.onClick.createDelegate(this, ["first"])
28736         });
28737         this.prev = this.addButton({
28738             tooltip: this.prevText,
28739             cls: "x-btn-icon x-grid-page-prev",
28740             disabled: true,
28741             handler: this.onClick.createDelegate(this, ["prev"])
28742         });
28743         //this.addSeparator();
28744         this.add(this.beforePageText);
28745         this.field = Roo.get(this.addDom({
28746            tag: "input",
28747            type: "text",
28748            size: "3",
28749            value: "1",
28750            cls: "x-grid-page-number"
28751         }).el);
28752         this.field.on("keydown", this.onPagingKeydown, this);
28753         this.field.on("focus", function(){this.dom.select();});
28754         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28755         this.field.setHeight(18);
28756         //this.addSeparator();
28757         this.next = this.addButton({
28758             tooltip: this.nextText,
28759             cls: "x-btn-icon x-grid-page-next",
28760             disabled: true,
28761             handler: this.onClick.createDelegate(this, ["next"])
28762         });
28763         this.last = this.addButton({
28764             tooltip: this.lastText,
28765             cls: "x-btn-icon x-grid-page-last",
28766             disabled: true,
28767             handler: this.onClick.createDelegate(this, ["last"])
28768         });
28769         //this.addSeparator();
28770         this.loading = this.addButton({
28771             tooltip: this.refreshText,
28772             cls: "x-btn-icon x-grid-loading",
28773             handler: this.onClick.createDelegate(this, ["refresh"])
28774         });
28775
28776         if(this.displayInfo){
28777             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28778         }
28779     },
28780
28781     // private
28782     updateInfo : function(){
28783         if(this.displayEl){
28784             var count = this.ds.getCount();
28785             var msg = count == 0 ?
28786                 this.emptyMsg :
28787                 String.format(
28788                     this.displayMsg,
28789                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28790                 );
28791             this.displayEl.update(msg);
28792         }
28793     },
28794
28795     // private
28796     onLoad : function(ds, r, o){
28797        this.cursor = o.params ? o.params.start : 0;
28798        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28799
28800        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28801        this.field.dom.value = ap;
28802        this.first.setDisabled(ap == 1);
28803        this.prev.setDisabled(ap == 1);
28804        this.next.setDisabled(ap == ps);
28805        this.last.setDisabled(ap == ps);
28806        this.loading.enable();
28807        this.updateInfo();
28808     },
28809
28810     // private
28811     getPageData : function(){
28812         var total = this.ds.getTotalCount();
28813         return {
28814             total : total,
28815             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28816             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28817         };
28818     },
28819
28820     // private
28821     onLoadError : function(){
28822         this.loading.enable();
28823     },
28824
28825     // private
28826     onPagingKeydown : function(e){
28827         var k = e.getKey();
28828         var d = this.getPageData();
28829         if(k == e.RETURN){
28830             var v = this.field.dom.value, pageNum;
28831             if(!v || isNaN(pageNum = parseInt(v, 10))){
28832                 this.field.dom.value = d.activePage;
28833                 return;
28834             }
28835             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28836             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28837             e.stopEvent();
28838         }
28839         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
28840         {
28841           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28842           this.field.dom.value = pageNum;
28843           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28844           e.stopEvent();
28845         }
28846         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28847         {
28848           var v = this.field.dom.value, pageNum; 
28849           var increment = (e.shiftKey) ? 10 : 1;
28850           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28851             increment *= -1;
28852           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28853             this.field.dom.value = d.activePage;
28854             return;
28855           }
28856           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28857           {
28858             this.field.dom.value = parseInt(v, 10) + increment;
28859             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28860             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28861           }
28862           e.stopEvent();
28863         }
28864     },
28865
28866     // private
28867     beforeLoad : function(){
28868         if(this.loading){
28869             this.loading.disable();
28870         }
28871     },
28872
28873     // private
28874     onClick : function(which){
28875         var ds = this.ds;
28876         switch(which){
28877             case "first":
28878                 ds.load({params:{start: 0, limit: this.pageSize}});
28879             break;
28880             case "prev":
28881                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28882             break;
28883             case "next":
28884                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28885             break;
28886             case "last":
28887                 var total = ds.getTotalCount();
28888                 var extra = total % this.pageSize;
28889                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28890                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28891             break;
28892             case "refresh":
28893                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28894             break;
28895         }
28896     },
28897
28898     /**
28899      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28900      * @param {Roo.data.Store} store The data store to unbind
28901      */
28902     unbind : function(ds){
28903         ds.un("beforeload", this.beforeLoad, this);
28904         ds.un("load", this.onLoad, this);
28905         ds.un("loadexception", this.onLoadError, this);
28906         ds.un("remove", this.updateInfo, this);
28907         ds.un("add", this.updateInfo, this);
28908         this.ds = undefined;
28909     },
28910
28911     /**
28912      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28913      * @param {Roo.data.Store} store The data store to bind
28914      */
28915     bind : function(ds){
28916         ds.on("beforeload", this.beforeLoad, this);
28917         ds.on("load", this.onLoad, this);
28918         ds.on("loadexception", this.onLoadError, this);
28919         ds.on("remove", this.updateInfo, this);
28920         ds.on("add", this.updateInfo, this);
28921         this.ds = ds;
28922     }
28923 });/*
28924  * Based on:
28925  * Ext JS Library 1.1.1
28926  * Copyright(c) 2006-2007, Ext JS, LLC.
28927  *
28928  * Originally Released Under LGPL - original licence link has changed is not relivant.
28929  *
28930  * Fork - LGPL
28931  * <script type="text/javascript">
28932  */
28933
28934 /**
28935  * @class Roo.Resizable
28936  * @extends Roo.util.Observable
28937  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28938  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28939  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
28940  * the element will be wrapped for you automatically.</p>
28941  * <p>Here is the list of valid resize handles:</p>
28942  * <pre>
28943 Value   Description
28944 ------  -------------------
28945  'n'     north
28946  's'     south
28947  'e'     east
28948  'w'     west
28949  'nw'    northwest
28950  'sw'    southwest
28951  'se'    southeast
28952  'ne'    northeast
28953  'hd'    horizontal drag
28954  'all'   all
28955 </pre>
28956  * <p>Here's an example showing the creation of a typical Resizable:</p>
28957  * <pre><code>
28958 var resizer = new Roo.Resizable("element-id", {
28959     handles: 'all',
28960     minWidth: 200,
28961     minHeight: 100,
28962     maxWidth: 500,
28963     maxHeight: 400,
28964     pinned: true
28965 });
28966 resizer.on("resize", myHandler);
28967 </code></pre>
28968  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28969  * resizer.east.setDisplayed(false);</p>
28970  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28971  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28972  * resize operation's new size (defaults to [0, 0])
28973  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28974  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28975  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28976  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28977  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28978  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28979  * @cfg {Number} width The width of the element in pixels (defaults to null)
28980  * @cfg {Number} height The height of the element in pixels (defaults to null)
28981  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28982  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28983  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28984  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28985  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28986  * in favor of the handles config option (defaults to false)
28987  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28988  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28989  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28990  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28991  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28992  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28993  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28994  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28995  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28996  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28997  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28998  * @constructor
28999  * Create a new resizable component
29000  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29001  * @param {Object} config configuration options
29002   */
29003 Roo.Resizable = function(el, config)
29004 {
29005     this.el = Roo.get(el);
29006
29007     if(config && config.wrap){
29008         config.resizeChild = this.el;
29009         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29010         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29011         this.el.setStyle("overflow", "hidden");
29012         this.el.setPositioning(config.resizeChild.getPositioning());
29013         config.resizeChild.clearPositioning();
29014         if(!config.width || !config.height){
29015             var csize = config.resizeChild.getSize();
29016             this.el.setSize(csize.width, csize.height);
29017         }
29018         if(config.pinned && !config.adjustments){
29019             config.adjustments = "auto";
29020         }
29021     }
29022
29023     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29024     this.proxy.unselectable();
29025     this.proxy.enableDisplayMode('block');
29026
29027     Roo.apply(this, config);
29028
29029     if(this.pinned){
29030         this.disableTrackOver = true;
29031         this.el.addClass("x-resizable-pinned");
29032     }
29033     // if the element isn't positioned, make it relative
29034     var position = this.el.getStyle("position");
29035     if(position != "absolute" && position != "fixed"){
29036         this.el.setStyle("position", "relative");
29037     }
29038     if(!this.handles){ // no handles passed, must be legacy style
29039         this.handles = 's,e,se';
29040         if(this.multiDirectional){
29041             this.handles += ',n,w';
29042         }
29043     }
29044     if(this.handles == "all"){
29045         this.handles = "n s e w ne nw se sw";
29046     }
29047     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29048     var ps = Roo.Resizable.positions;
29049     for(var i = 0, len = hs.length; i < len; i++){
29050         if(hs[i] && ps[hs[i]]){
29051             var pos = ps[hs[i]];
29052             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29053         }
29054     }
29055     // legacy
29056     this.corner = this.southeast;
29057     
29058     // updateBox = the box can move..
29059     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29060         this.updateBox = true;
29061     }
29062
29063     this.activeHandle = null;
29064
29065     if(this.resizeChild){
29066         if(typeof this.resizeChild == "boolean"){
29067             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29068         }else{
29069             this.resizeChild = Roo.get(this.resizeChild, true);
29070         }
29071     }
29072     
29073     if(this.adjustments == "auto"){
29074         var rc = this.resizeChild;
29075         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29076         if(rc && (hw || hn)){
29077             rc.position("relative");
29078             rc.setLeft(hw ? hw.el.getWidth() : 0);
29079             rc.setTop(hn ? hn.el.getHeight() : 0);
29080         }
29081         this.adjustments = [
29082             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29083             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29084         ];
29085     }
29086
29087     if(this.draggable){
29088         this.dd = this.dynamic ?
29089             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29090         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29091     }
29092
29093     // public events
29094     this.addEvents({
29095         /**
29096          * @event beforeresize
29097          * Fired before resize is allowed. Set enabled to false to cancel resize.
29098          * @param {Roo.Resizable} this
29099          * @param {Roo.EventObject} e The mousedown event
29100          */
29101         "beforeresize" : true,
29102         /**
29103          * @event resizing
29104          * Fired a resizing.
29105          * @param {Roo.Resizable} this
29106          * @param {Number} x The new x position
29107          * @param {Number} y The new y position
29108          * @param {Number} w The new w width
29109          * @param {Number} h The new h hight
29110          * @param {Roo.EventObject} e The mouseup event
29111          */
29112         "resizing" : true,
29113         /**
29114          * @event resize
29115          * Fired after a resize.
29116          * @param {Roo.Resizable} this
29117          * @param {Number} width The new width
29118          * @param {Number} height The new height
29119          * @param {Roo.EventObject} e The mouseup event
29120          */
29121         "resize" : true
29122     });
29123
29124     if(this.width !== null && this.height !== null){
29125         this.resizeTo(this.width, this.height);
29126     }else{
29127         this.updateChildSize();
29128     }
29129     if(Roo.isIE){
29130         this.el.dom.style.zoom = 1;
29131     }
29132     Roo.Resizable.superclass.constructor.call(this);
29133 };
29134
29135 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29136         resizeChild : false,
29137         adjustments : [0, 0],
29138         minWidth : 5,
29139         minHeight : 5,
29140         maxWidth : 10000,
29141         maxHeight : 10000,
29142         enabled : true,
29143         animate : false,
29144         duration : .35,
29145         dynamic : false,
29146         handles : false,
29147         multiDirectional : false,
29148         disableTrackOver : false,
29149         easing : 'easeOutStrong',
29150         widthIncrement : 0,
29151         heightIncrement : 0,
29152         pinned : false,
29153         width : null,
29154         height : null,
29155         preserveRatio : false,
29156         transparent: false,
29157         minX: 0,
29158         minY: 0,
29159         draggable: false,
29160
29161         /**
29162          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29163          */
29164         constrainTo: undefined,
29165         /**
29166          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29167          */
29168         resizeRegion: undefined,
29169
29170
29171     /**
29172      * Perform a manual resize
29173      * @param {Number} width
29174      * @param {Number} height
29175      */
29176     resizeTo : function(width, height){
29177         this.el.setSize(width, height);
29178         this.updateChildSize();
29179         this.fireEvent("resize", this, width, height, null);
29180     },
29181
29182     // private
29183     startSizing : function(e, handle){
29184         this.fireEvent("beforeresize", this, e);
29185         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29186
29187             if(!this.overlay){
29188                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29189                 this.overlay.unselectable();
29190                 this.overlay.enableDisplayMode("block");
29191                 this.overlay.on("mousemove", this.onMouseMove, this);
29192                 this.overlay.on("mouseup", this.onMouseUp, this);
29193             }
29194             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29195
29196             this.resizing = true;
29197             this.startBox = this.el.getBox();
29198             this.startPoint = e.getXY();
29199             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29200                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29201
29202             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29203             this.overlay.show();
29204
29205             if(this.constrainTo) {
29206                 var ct = Roo.get(this.constrainTo);
29207                 this.resizeRegion = ct.getRegion().adjust(
29208                     ct.getFrameWidth('t'),
29209                     ct.getFrameWidth('l'),
29210                     -ct.getFrameWidth('b'),
29211                     -ct.getFrameWidth('r')
29212                 );
29213             }
29214
29215             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29216             this.proxy.show();
29217             this.proxy.setBox(this.startBox);
29218             if(!this.dynamic){
29219                 this.proxy.setStyle('visibility', 'visible');
29220             }
29221         }
29222     },
29223
29224     // private
29225     onMouseDown : function(handle, e){
29226         if(this.enabled){
29227             e.stopEvent();
29228             this.activeHandle = handle;
29229             this.startSizing(e, handle);
29230         }
29231     },
29232
29233     // private
29234     onMouseUp : function(e){
29235         var size = this.resizeElement();
29236         this.resizing = false;
29237         this.handleOut();
29238         this.overlay.hide();
29239         this.proxy.hide();
29240         this.fireEvent("resize", this, size.width, size.height, e);
29241     },
29242
29243     // private
29244     updateChildSize : function(){
29245         
29246         if(this.resizeChild){
29247             var el = this.el;
29248             var child = this.resizeChild;
29249             var adj = this.adjustments;
29250             if(el.dom.offsetWidth){
29251                 var b = el.getSize(true);
29252                 child.setSize(b.width+adj[0], b.height+adj[1]);
29253             }
29254             // Second call here for IE
29255             // The first call enables instant resizing and
29256             // the second call corrects scroll bars if they
29257             // exist
29258             if(Roo.isIE){
29259                 setTimeout(function(){
29260                     if(el.dom.offsetWidth){
29261                         var b = el.getSize(true);
29262                         child.setSize(b.width+adj[0], b.height+adj[1]);
29263                     }
29264                 }, 10);
29265             }
29266         }
29267     },
29268
29269     // private
29270     snap : function(value, inc, min){
29271         if(!inc || !value) return value;
29272         var newValue = value;
29273         var m = value % inc;
29274         if(m > 0){
29275             if(m > (inc/2)){
29276                 newValue = value + (inc-m);
29277             }else{
29278                 newValue = value - m;
29279             }
29280         }
29281         return Math.max(min, newValue);
29282     },
29283
29284     // private
29285     resizeElement : function(){
29286         var box = this.proxy.getBox();
29287         if(this.updateBox){
29288             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29289         }else{
29290             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29291         }
29292         this.updateChildSize();
29293         if(!this.dynamic){
29294             this.proxy.hide();
29295         }
29296         return box;
29297     },
29298
29299     // private
29300     constrain : function(v, diff, m, mx){
29301         if(v - diff < m){
29302             diff = v - m;
29303         }else if(v - diff > mx){
29304             diff = mx - v;
29305         }
29306         return diff;
29307     },
29308
29309     // private
29310     onMouseMove : function(e){
29311         
29312         if(this.enabled){
29313             try{// try catch so if something goes wrong the user doesn't get hung
29314
29315             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29316                 return;
29317             }
29318
29319             //var curXY = this.startPoint;
29320             var curSize = this.curSize || this.startBox;
29321             var x = this.startBox.x, y = this.startBox.y;
29322             var ox = x, oy = y;
29323             var w = curSize.width, h = curSize.height;
29324             var ow = w, oh = h;
29325             var mw = this.minWidth, mh = this.minHeight;
29326             var mxw = this.maxWidth, mxh = this.maxHeight;
29327             var wi = this.widthIncrement;
29328             var hi = this.heightIncrement;
29329
29330             var eventXY = e.getXY();
29331             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29332             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29333
29334             var pos = this.activeHandle.position;
29335
29336             switch(pos){
29337                 case "east":
29338                     w += diffX;
29339                     w = Math.min(Math.max(mw, w), mxw);
29340                     break;
29341              
29342                 case "south":
29343                     h += diffY;
29344                     h = Math.min(Math.max(mh, h), mxh);
29345                     break;
29346                 case "southeast":
29347                     w += diffX;
29348                     h += diffY;
29349                     w = Math.min(Math.max(mw, w), mxw);
29350                     h = Math.min(Math.max(mh, h), mxh);
29351                     break;
29352                 case "north":
29353                     diffY = this.constrain(h, diffY, mh, mxh);
29354                     y += diffY;
29355                     h -= diffY;
29356                     break;
29357                 case "hdrag":
29358                     
29359                     if (wi) {
29360                         var adiffX = Math.abs(diffX);
29361                         var sub = (adiffX % wi); // how much 
29362                         if (sub > (wi/2)) { // far enough to snap
29363                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29364                         } else {
29365                             // remove difference.. 
29366                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29367                         }
29368                     }
29369                     x += diffX;
29370                     x = Math.max(this.minX, x);
29371                     break;
29372                 case "west":
29373                     diffX = this.constrain(w, diffX, mw, mxw);
29374                     x += diffX;
29375                     w -= diffX;
29376                     break;
29377                 case "northeast":
29378                     w += diffX;
29379                     w = Math.min(Math.max(mw, w), mxw);
29380                     diffY = this.constrain(h, diffY, mh, mxh);
29381                     y += diffY;
29382                     h -= diffY;
29383                     break;
29384                 case "northwest":
29385                     diffX = this.constrain(w, diffX, mw, mxw);
29386                     diffY = this.constrain(h, diffY, mh, mxh);
29387                     y += diffY;
29388                     h -= diffY;
29389                     x += diffX;
29390                     w -= diffX;
29391                     break;
29392                case "southwest":
29393                     diffX = this.constrain(w, diffX, mw, mxw);
29394                     h += diffY;
29395                     h = Math.min(Math.max(mh, h), mxh);
29396                     x += diffX;
29397                     w -= diffX;
29398                     break;
29399             }
29400
29401             var sw = this.snap(w, wi, mw);
29402             var sh = this.snap(h, hi, mh);
29403             if(sw != w || sh != h){
29404                 switch(pos){
29405                     case "northeast":
29406                         y -= sh - h;
29407                     break;
29408                     case "north":
29409                         y -= sh - h;
29410                         break;
29411                     case "southwest":
29412                         x -= sw - w;
29413                     break;
29414                     case "west":
29415                         x -= sw - w;
29416                         break;
29417                     case "northwest":
29418                         x -= sw - w;
29419                         y -= sh - h;
29420                     break;
29421                 }
29422                 w = sw;
29423                 h = sh;
29424             }
29425
29426             if(this.preserveRatio){
29427                 switch(pos){
29428                     case "southeast":
29429                     case "east":
29430                         h = oh * (w/ow);
29431                         h = Math.min(Math.max(mh, h), mxh);
29432                         w = ow * (h/oh);
29433                        break;
29434                     case "south":
29435                         w = ow * (h/oh);
29436                         w = Math.min(Math.max(mw, w), mxw);
29437                         h = oh * (w/ow);
29438                         break;
29439                     case "northeast":
29440                         w = ow * (h/oh);
29441                         w = Math.min(Math.max(mw, w), mxw);
29442                         h = oh * (w/ow);
29443                     break;
29444                     case "north":
29445                         var tw = w;
29446                         w = ow * (h/oh);
29447                         w = Math.min(Math.max(mw, w), mxw);
29448                         h = oh * (w/ow);
29449                         x += (tw - w) / 2;
29450                         break;
29451                     case "southwest":
29452                         h = oh * (w/ow);
29453                         h = Math.min(Math.max(mh, h), mxh);
29454                         var tw = w;
29455                         w = ow * (h/oh);
29456                         x += tw - w;
29457                         break;
29458                     case "west":
29459                         var th = h;
29460                         h = oh * (w/ow);
29461                         h = Math.min(Math.max(mh, h), mxh);
29462                         y += (th - h) / 2;
29463                         var tw = w;
29464                         w = ow * (h/oh);
29465                         x += tw - w;
29466                        break;
29467                     case "northwest":
29468                         var tw = w;
29469                         var th = h;
29470                         h = oh * (w/ow);
29471                         h = Math.min(Math.max(mh, h), mxh);
29472                         w = ow * (h/oh);
29473                         y += th - h;
29474                         x += tw - w;
29475                        break;
29476
29477                 }
29478             }
29479             if (pos == 'hdrag') {
29480                 w = ow;
29481             }
29482             this.proxy.setBounds(x, y, w, h);
29483             if(this.dynamic){
29484                 this.resizeElement();
29485             }
29486             }catch(e){}
29487         }
29488         this.fireEvent("resizing", this, x, y, w, h, e);
29489     },
29490
29491     // private
29492     handleOver : function(){
29493         if(this.enabled){
29494             this.el.addClass("x-resizable-over");
29495         }
29496     },
29497
29498     // private
29499     handleOut : function(){
29500         if(!this.resizing){
29501             this.el.removeClass("x-resizable-over");
29502         }
29503     },
29504
29505     /**
29506      * Returns the element this component is bound to.
29507      * @return {Roo.Element}
29508      */
29509     getEl : function(){
29510         return this.el;
29511     },
29512
29513     /**
29514      * Returns the resizeChild element (or null).
29515      * @return {Roo.Element}
29516      */
29517     getResizeChild : function(){
29518         return this.resizeChild;
29519     },
29520     groupHandler : function()
29521     {
29522         
29523     },
29524     /**
29525      * Destroys this resizable. If the element was wrapped and
29526      * removeEl is not true then the element remains.
29527      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29528      */
29529     destroy : function(removeEl){
29530         this.proxy.remove();
29531         if(this.overlay){
29532             this.overlay.removeAllListeners();
29533             this.overlay.remove();
29534         }
29535         var ps = Roo.Resizable.positions;
29536         for(var k in ps){
29537             if(typeof ps[k] != "function" && this[ps[k]]){
29538                 var h = this[ps[k]];
29539                 h.el.removeAllListeners();
29540                 h.el.remove();
29541             }
29542         }
29543         if(removeEl){
29544             this.el.update("");
29545             this.el.remove();
29546         }
29547     }
29548 });
29549
29550 // private
29551 // hash to map config positions to true positions
29552 Roo.Resizable.positions = {
29553     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29554     hd: "hdrag"
29555 };
29556
29557 // private
29558 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29559     if(!this.tpl){
29560         // only initialize the template if resizable is used
29561         var tpl = Roo.DomHelper.createTemplate(
29562             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29563         );
29564         tpl.compile();
29565         Roo.Resizable.Handle.prototype.tpl = tpl;
29566     }
29567     this.position = pos;
29568     this.rz = rz;
29569     // show north drag fro topdra
29570     var handlepos = pos == 'hdrag' ? 'north' : pos;
29571     
29572     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29573     if (pos == 'hdrag') {
29574         this.el.setStyle('cursor', 'pointer');
29575     }
29576     this.el.unselectable();
29577     if(transparent){
29578         this.el.setOpacity(0);
29579     }
29580     this.el.on("mousedown", this.onMouseDown, this);
29581     if(!disableTrackOver){
29582         this.el.on("mouseover", this.onMouseOver, this);
29583         this.el.on("mouseout", this.onMouseOut, this);
29584     }
29585 };
29586
29587 // private
29588 Roo.Resizable.Handle.prototype = {
29589     afterResize : function(rz){
29590         Roo.log('after?');
29591         // do nothing
29592     },
29593     // private
29594     onMouseDown : function(e){
29595         this.rz.onMouseDown(this, e);
29596     },
29597     // private
29598     onMouseOver : function(e){
29599         this.rz.handleOver(this, e);
29600     },
29601     // private
29602     onMouseOut : function(e){
29603         this.rz.handleOut(this, e);
29604     }
29605 };/*
29606  * Based on:
29607  * Ext JS Library 1.1.1
29608  * Copyright(c) 2006-2007, Ext JS, LLC.
29609  *
29610  * Originally Released Under LGPL - original licence link has changed is not relivant.
29611  *
29612  * Fork - LGPL
29613  * <script type="text/javascript">
29614  */
29615
29616 /**
29617  * @class Roo.Editor
29618  * @extends Roo.Component
29619  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29620  * @constructor
29621  * Create a new Editor
29622  * @param {Roo.form.Field} field The Field object (or descendant)
29623  * @param {Object} config The config object
29624  */
29625 Roo.Editor = function(field, config){
29626     Roo.Editor.superclass.constructor.call(this, config);
29627     this.field = field;
29628     this.addEvents({
29629         /**
29630              * @event beforestartedit
29631              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29632              * false from the handler of this event.
29633              * @param {Editor} this
29634              * @param {Roo.Element} boundEl The underlying element bound to this editor
29635              * @param {Mixed} value The field value being set
29636              */
29637         "beforestartedit" : true,
29638         /**
29639              * @event startedit
29640              * Fires when this editor is displayed
29641              * @param {Roo.Element} boundEl The underlying element bound to this editor
29642              * @param {Mixed} value The starting field value
29643              */
29644         "startedit" : true,
29645         /**
29646              * @event beforecomplete
29647              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29648              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29649              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29650              * event will not fire since no edit actually occurred.
29651              * @param {Editor} this
29652              * @param {Mixed} value The current field value
29653              * @param {Mixed} startValue The original field value
29654              */
29655         "beforecomplete" : true,
29656         /**
29657              * @event complete
29658              * Fires after editing is complete and any changed value has been written to the underlying field.
29659              * @param {Editor} this
29660              * @param {Mixed} value The current field value
29661              * @param {Mixed} startValue The original field value
29662              */
29663         "complete" : true,
29664         /**
29665          * @event specialkey
29666          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29667          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29668          * @param {Roo.form.Field} this
29669          * @param {Roo.EventObject} e The event object
29670          */
29671         "specialkey" : true
29672     });
29673 };
29674
29675 Roo.extend(Roo.Editor, Roo.Component, {
29676     /**
29677      * @cfg {Boolean/String} autosize
29678      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29679      * or "height" to adopt the height only (defaults to false)
29680      */
29681     /**
29682      * @cfg {Boolean} revertInvalid
29683      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29684      * validation fails (defaults to true)
29685      */
29686     /**
29687      * @cfg {Boolean} ignoreNoChange
29688      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29689      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29690      * will never be ignored.
29691      */
29692     /**
29693      * @cfg {Boolean} hideEl
29694      * False to keep the bound element visible while the editor is displayed (defaults to true)
29695      */
29696     /**
29697      * @cfg {Mixed} value
29698      * The data value of the underlying field (defaults to "")
29699      */
29700     value : "",
29701     /**
29702      * @cfg {String} alignment
29703      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29704      */
29705     alignment: "c-c?",
29706     /**
29707      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29708      * for bottom-right shadow (defaults to "frame")
29709      */
29710     shadow : "frame",
29711     /**
29712      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29713      */
29714     constrain : false,
29715     /**
29716      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29717      */
29718     completeOnEnter : false,
29719     /**
29720      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29721      */
29722     cancelOnEsc : false,
29723     /**
29724      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29725      */
29726     updateEl : false,
29727
29728     // private
29729     onRender : function(ct, position){
29730         this.el = new Roo.Layer({
29731             shadow: this.shadow,
29732             cls: "x-editor",
29733             parentEl : ct,
29734             shim : this.shim,
29735             shadowOffset:4,
29736             id: this.id,
29737             constrain: this.constrain
29738         });
29739         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29740         if(this.field.msgTarget != 'title'){
29741             this.field.msgTarget = 'qtip';
29742         }
29743         this.field.render(this.el);
29744         if(Roo.isGecko){
29745             this.field.el.dom.setAttribute('autocomplete', 'off');
29746         }
29747         this.field.on("specialkey", this.onSpecialKey, this);
29748         if(this.swallowKeys){
29749             this.field.el.swallowEvent(['keydown','keypress']);
29750         }
29751         this.field.show();
29752         this.field.on("blur", this.onBlur, this);
29753         if(this.field.grow){
29754             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29755         }
29756     },
29757
29758     onSpecialKey : function(field, e)
29759     {
29760         //Roo.log('editor onSpecialKey');
29761         if(this.completeOnEnter && e.getKey() == e.ENTER){
29762             e.stopEvent();
29763             this.completeEdit();
29764             return;
29765         }
29766         // do not fire special key otherwise it might hide close the editor...
29767         if(e.getKey() == e.ENTER){    
29768             return;
29769         }
29770         if(this.cancelOnEsc && e.getKey() == e.ESC){
29771             this.cancelEdit();
29772             return;
29773         } 
29774         this.fireEvent('specialkey', field, e);
29775     
29776     },
29777
29778     /**
29779      * Starts the editing process and shows the editor.
29780      * @param {String/HTMLElement/Element} el The element to edit
29781      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29782       * to the innerHTML of el.
29783      */
29784     startEdit : function(el, value){
29785         if(this.editing){
29786             this.completeEdit();
29787         }
29788         this.boundEl = Roo.get(el);
29789         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29790         if(!this.rendered){
29791             this.render(this.parentEl || document.body);
29792         }
29793         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29794             return;
29795         }
29796         this.startValue = v;
29797         this.field.setValue(v);
29798         if(this.autoSize){
29799             var sz = this.boundEl.getSize();
29800             switch(this.autoSize){
29801                 case "width":
29802                 this.setSize(sz.width,  "");
29803                 break;
29804                 case "height":
29805                 this.setSize("",  sz.height);
29806                 break;
29807                 default:
29808                 this.setSize(sz.width,  sz.height);
29809             }
29810         }
29811         this.el.alignTo(this.boundEl, this.alignment);
29812         this.editing = true;
29813         if(Roo.QuickTips){
29814             Roo.QuickTips.disable();
29815         }
29816         this.show();
29817     },
29818
29819     /**
29820      * Sets the height and width of this editor.
29821      * @param {Number} width The new width
29822      * @param {Number} height The new height
29823      */
29824     setSize : function(w, h){
29825         this.field.setSize(w, h);
29826         if(this.el){
29827             this.el.sync();
29828         }
29829     },
29830
29831     /**
29832      * Realigns the editor to the bound field based on the current alignment config value.
29833      */
29834     realign : function(){
29835         this.el.alignTo(this.boundEl, this.alignment);
29836     },
29837
29838     /**
29839      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29840      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29841      */
29842     completeEdit : function(remainVisible){
29843         if(!this.editing){
29844             return;
29845         }
29846         var v = this.getValue();
29847         if(this.revertInvalid !== false && !this.field.isValid()){
29848             v = this.startValue;
29849             this.cancelEdit(true);
29850         }
29851         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29852             this.editing = false;
29853             this.hide();
29854             return;
29855         }
29856         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29857             this.editing = false;
29858             if(this.updateEl && this.boundEl){
29859                 this.boundEl.update(v);
29860             }
29861             if(remainVisible !== true){
29862                 this.hide();
29863             }
29864             this.fireEvent("complete", this, v, this.startValue);
29865         }
29866     },
29867
29868     // private
29869     onShow : function(){
29870         this.el.show();
29871         if(this.hideEl !== false){
29872             this.boundEl.hide();
29873         }
29874         this.field.show();
29875         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29876             this.fixIEFocus = true;
29877             this.deferredFocus.defer(50, this);
29878         }else{
29879             this.field.focus();
29880         }
29881         this.fireEvent("startedit", this.boundEl, this.startValue);
29882     },
29883
29884     deferredFocus : function(){
29885         if(this.editing){
29886             this.field.focus();
29887         }
29888     },
29889
29890     /**
29891      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29892      * reverted to the original starting value.
29893      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29894      * cancel (defaults to false)
29895      */
29896     cancelEdit : function(remainVisible){
29897         if(this.editing){
29898             this.setValue(this.startValue);
29899             if(remainVisible !== true){
29900                 this.hide();
29901             }
29902         }
29903     },
29904
29905     // private
29906     onBlur : function(){
29907         if(this.allowBlur !== true && this.editing){
29908             this.completeEdit();
29909         }
29910     },
29911
29912     // private
29913     onHide : function(){
29914         if(this.editing){
29915             this.completeEdit();
29916             return;
29917         }
29918         this.field.blur();
29919         if(this.field.collapse){
29920             this.field.collapse();
29921         }
29922         this.el.hide();
29923         if(this.hideEl !== false){
29924             this.boundEl.show();
29925         }
29926         if(Roo.QuickTips){
29927             Roo.QuickTips.enable();
29928         }
29929     },
29930
29931     /**
29932      * Sets the data value of the editor
29933      * @param {Mixed} value Any valid value supported by the underlying field
29934      */
29935     setValue : function(v){
29936         this.field.setValue(v);
29937     },
29938
29939     /**
29940      * Gets the data value of the editor
29941      * @return {Mixed} The data value
29942      */
29943     getValue : function(){
29944         return this.field.getValue();
29945     }
29946 });/*
29947  * Based on:
29948  * Ext JS Library 1.1.1
29949  * Copyright(c) 2006-2007, Ext JS, LLC.
29950  *
29951  * Originally Released Under LGPL - original licence link has changed is not relivant.
29952  *
29953  * Fork - LGPL
29954  * <script type="text/javascript">
29955  */
29956  
29957 /**
29958  * @class Roo.BasicDialog
29959  * @extends Roo.util.Observable
29960  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29961  * <pre><code>
29962 var dlg = new Roo.BasicDialog("my-dlg", {
29963     height: 200,
29964     width: 300,
29965     minHeight: 100,
29966     minWidth: 150,
29967     modal: true,
29968     proxyDrag: true,
29969     shadow: true
29970 });
29971 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29972 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29973 dlg.addButton('Cancel', dlg.hide, dlg);
29974 dlg.show();
29975 </code></pre>
29976   <b>A Dialog should always be a direct child of the body element.</b>
29977  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29978  * @cfg {String} title Default text to display in the title bar (defaults to null)
29979  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29980  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29981  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29982  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29983  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29984  * (defaults to null with no animation)
29985  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29986  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29987  * property for valid values (defaults to 'all')
29988  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29989  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29990  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29991  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29992  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29993  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29994  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29995  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29996  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29997  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29998  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29999  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30000  * draggable = true (defaults to false)
30001  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30002  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30003  * shadow (defaults to false)
30004  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30005  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30006  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30007  * @cfg {Array} buttons Array of buttons
30008  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30009  * @constructor
30010  * Create a new BasicDialog.
30011  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30012  * @param {Object} config Configuration options
30013  */
30014 Roo.BasicDialog = function(el, config){
30015     this.el = Roo.get(el);
30016     var dh = Roo.DomHelper;
30017     if(!this.el && config && config.autoCreate){
30018         if(typeof config.autoCreate == "object"){
30019             if(!config.autoCreate.id){
30020                 config.autoCreate.id = el;
30021             }
30022             this.el = dh.append(document.body,
30023                         config.autoCreate, true);
30024         }else{
30025             this.el = dh.append(document.body,
30026                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30027         }
30028     }
30029     el = this.el;
30030     el.setDisplayed(true);
30031     el.hide = this.hideAction;
30032     this.id = el.id;
30033     el.addClass("x-dlg");
30034
30035     Roo.apply(this, config);
30036
30037     this.proxy = el.createProxy("x-dlg-proxy");
30038     this.proxy.hide = this.hideAction;
30039     this.proxy.setOpacity(.5);
30040     this.proxy.hide();
30041
30042     if(config.width){
30043         el.setWidth(config.width);
30044     }
30045     if(config.height){
30046         el.setHeight(config.height);
30047     }
30048     this.size = el.getSize();
30049     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30050         this.xy = [config.x,config.y];
30051     }else{
30052         this.xy = el.getCenterXY(true);
30053     }
30054     /** The header element @type Roo.Element */
30055     this.header = el.child("> .x-dlg-hd");
30056     /** The body element @type Roo.Element */
30057     this.body = el.child("> .x-dlg-bd");
30058     /** The footer element @type Roo.Element */
30059     this.footer = el.child("> .x-dlg-ft");
30060
30061     if(!this.header){
30062         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30063     }
30064     if(!this.body){
30065         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30066     }
30067
30068     this.header.unselectable();
30069     if(this.title){
30070         this.header.update(this.title);
30071     }
30072     // this element allows the dialog to be focused for keyboard event
30073     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30074     this.focusEl.swallowEvent("click", true);
30075
30076     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30077
30078     // wrap the body and footer for special rendering
30079     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30080     if(this.footer){
30081         this.bwrap.dom.appendChild(this.footer.dom);
30082     }
30083
30084     this.bg = this.el.createChild({
30085         tag: "div", cls:"x-dlg-bg",
30086         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30087     });
30088     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30089
30090
30091     if(this.autoScroll !== false && !this.autoTabs){
30092         this.body.setStyle("overflow", "auto");
30093     }
30094
30095     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30096
30097     if(this.closable !== false){
30098         this.el.addClass("x-dlg-closable");
30099         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30100         this.close.on("click", this.closeClick, this);
30101         this.close.addClassOnOver("x-dlg-close-over");
30102     }
30103     if(this.collapsible !== false){
30104         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30105         this.collapseBtn.on("click", this.collapseClick, this);
30106         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30107         this.header.on("dblclick", this.collapseClick, this);
30108     }
30109     if(this.resizable !== false){
30110         this.el.addClass("x-dlg-resizable");
30111         this.resizer = new Roo.Resizable(el, {
30112             minWidth: this.minWidth || 80,
30113             minHeight:this.minHeight || 80,
30114             handles: this.resizeHandles || "all",
30115             pinned: true
30116         });
30117         this.resizer.on("beforeresize", this.beforeResize, this);
30118         this.resizer.on("resize", this.onResize, this);
30119     }
30120     if(this.draggable !== false){
30121         el.addClass("x-dlg-draggable");
30122         if (!this.proxyDrag) {
30123             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30124         }
30125         else {
30126             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30127         }
30128         dd.setHandleElId(this.header.id);
30129         dd.endDrag = this.endMove.createDelegate(this);
30130         dd.startDrag = this.startMove.createDelegate(this);
30131         dd.onDrag = this.onDrag.createDelegate(this);
30132         dd.scroll = false;
30133         this.dd = dd;
30134     }
30135     if(this.modal){
30136         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30137         this.mask.enableDisplayMode("block");
30138         this.mask.hide();
30139         this.el.addClass("x-dlg-modal");
30140     }
30141     if(this.shadow){
30142         this.shadow = new Roo.Shadow({
30143             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30144             offset : this.shadowOffset
30145         });
30146     }else{
30147         this.shadowOffset = 0;
30148     }
30149     if(Roo.useShims && this.shim !== false){
30150         this.shim = this.el.createShim();
30151         this.shim.hide = this.hideAction;
30152         this.shim.hide();
30153     }else{
30154         this.shim = false;
30155     }
30156     if(this.autoTabs){
30157         this.initTabs();
30158     }
30159     if (this.buttons) { 
30160         var bts= this.buttons;
30161         this.buttons = [];
30162         Roo.each(bts, function(b) {
30163             this.addButton(b);
30164         }, this);
30165     }
30166     
30167     
30168     this.addEvents({
30169         /**
30170          * @event keydown
30171          * Fires when a key is pressed
30172          * @param {Roo.BasicDialog} this
30173          * @param {Roo.EventObject} e
30174          */
30175         "keydown" : true,
30176         /**
30177          * @event move
30178          * Fires when this dialog is moved by the user.
30179          * @param {Roo.BasicDialog} this
30180          * @param {Number} x The new page X
30181          * @param {Number} y The new page Y
30182          */
30183         "move" : true,
30184         /**
30185          * @event resize
30186          * Fires when this dialog is resized by the user.
30187          * @param {Roo.BasicDialog} this
30188          * @param {Number} width The new width
30189          * @param {Number} height The new height
30190          */
30191         "resize" : true,
30192         /**
30193          * @event beforehide
30194          * Fires before this dialog is hidden.
30195          * @param {Roo.BasicDialog} this
30196          */
30197         "beforehide" : true,
30198         /**
30199          * @event hide
30200          * Fires when this dialog is hidden.
30201          * @param {Roo.BasicDialog} this
30202          */
30203         "hide" : true,
30204         /**
30205          * @event beforeshow
30206          * Fires before this dialog is shown.
30207          * @param {Roo.BasicDialog} this
30208          */
30209         "beforeshow" : true,
30210         /**
30211          * @event show
30212          * Fires when this dialog is shown.
30213          * @param {Roo.BasicDialog} this
30214          */
30215         "show" : true
30216     });
30217     el.on("keydown", this.onKeyDown, this);
30218     el.on("mousedown", this.toFront, this);
30219     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30220     this.el.hide();
30221     Roo.DialogManager.register(this);
30222     Roo.BasicDialog.superclass.constructor.call(this);
30223 };
30224
30225 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30226     shadowOffset: Roo.isIE ? 6 : 5,
30227     minHeight: 80,
30228     minWidth: 200,
30229     minButtonWidth: 75,
30230     defaultButton: null,
30231     buttonAlign: "right",
30232     tabTag: 'div',
30233     firstShow: true,
30234
30235     /**
30236      * Sets the dialog title text
30237      * @param {String} text The title text to display
30238      * @return {Roo.BasicDialog} this
30239      */
30240     setTitle : function(text){
30241         this.header.update(text);
30242         return this;
30243     },
30244
30245     // private
30246     closeClick : function(){
30247         this.hide();
30248     },
30249
30250     // private
30251     collapseClick : function(){
30252         this[this.collapsed ? "expand" : "collapse"]();
30253     },
30254
30255     /**
30256      * Collapses the dialog to its minimized state (only the title bar is visible).
30257      * Equivalent to the user clicking the collapse dialog button.
30258      */
30259     collapse : function(){
30260         if(!this.collapsed){
30261             this.collapsed = true;
30262             this.el.addClass("x-dlg-collapsed");
30263             this.restoreHeight = this.el.getHeight();
30264             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30265         }
30266     },
30267
30268     /**
30269      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30270      * clicking the expand dialog button.
30271      */
30272     expand : function(){
30273         if(this.collapsed){
30274             this.collapsed = false;
30275             this.el.removeClass("x-dlg-collapsed");
30276             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30277         }
30278     },
30279
30280     /**
30281      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30282      * @return {Roo.TabPanel} The tabs component
30283      */
30284     initTabs : function(){
30285         var tabs = this.getTabs();
30286         while(tabs.getTab(0)){
30287             tabs.removeTab(0);
30288         }
30289         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30290             var dom = el.dom;
30291             tabs.addTab(Roo.id(dom), dom.title);
30292             dom.title = "";
30293         });
30294         tabs.activate(0);
30295         return tabs;
30296     },
30297
30298     // private
30299     beforeResize : function(){
30300         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30301     },
30302
30303     // private
30304     onResize : function(){
30305         this.refreshSize();
30306         this.syncBodyHeight();
30307         this.adjustAssets();
30308         this.focus();
30309         this.fireEvent("resize", this, this.size.width, this.size.height);
30310     },
30311
30312     // private
30313     onKeyDown : function(e){
30314         if(this.isVisible()){
30315             this.fireEvent("keydown", this, e);
30316         }
30317     },
30318
30319     /**
30320      * Resizes the dialog.
30321      * @param {Number} width
30322      * @param {Number} height
30323      * @return {Roo.BasicDialog} this
30324      */
30325     resizeTo : function(width, height){
30326         this.el.setSize(width, height);
30327         this.size = {width: width, height: height};
30328         this.syncBodyHeight();
30329         if(this.fixedcenter){
30330             this.center();
30331         }
30332         if(this.isVisible()){
30333             this.constrainXY();
30334             this.adjustAssets();
30335         }
30336         this.fireEvent("resize", this, width, height);
30337         return this;
30338     },
30339
30340
30341     /**
30342      * Resizes the dialog to fit the specified content size.
30343      * @param {Number} width
30344      * @param {Number} height
30345      * @return {Roo.BasicDialog} this
30346      */
30347     setContentSize : function(w, h){
30348         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30349         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30350         //if(!this.el.isBorderBox()){
30351             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30352             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30353         //}
30354         if(this.tabs){
30355             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30356             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30357         }
30358         this.resizeTo(w, h);
30359         return this;
30360     },
30361
30362     /**
30363      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30364      * executed in response to a particular key being pressed while the dialog is active.
30365      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30366      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30367      * @param {Function} fn The function to call
30368      * @param {Object} scope (optional) The scope of the function
30369      * @return {Roo.BasicDialog} this
30370      */
30371     addKeyListener : function(key, fn, scope){
30372         var keyCode, shift, ctrl, alt;
30373         if(typeof key == "object" && !(key instanceof Array)){
30374             keyCode = key["key"];
30375             shift = key["shift"];
30376             ctrl = key["ctrl"];
30377             alt = key["alt"];
30378         }else{
30379             keyCode = key;
30380         }
30381         var handler = function(dlg, e){
30382             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30383                 var k = e.getKey();
30384                 if(keyCode instanceof Array){
30385                     for(var i = 0, len = keyCode.length; i < len; i++){
30386                         if(keyCode[i] == k){
30387                           fn.call(scope || window, dlg, k, e);
30388                           return;
30389                         }
30390                     }
30391                 }else{
30392                     if(k == keyCode){
30393                         fn.call(scope || window, dlg, k, e);
30394                     }
30395                 }
30396             }
30397         };
30398         this.on("keydown", handler);
30399         return this;
30400     },
30401
30402     /**
30403      * Returns the TabPanel component (creates it if it doesn't exist).
30404      * Note: If you wish to simply check for the existence of tabs without creating them,
30405      * check for a null 'tabs' property.
30406      * @return {Roo.TabPanel} The tabs component
30407      */
30408     getTabs : function(){
30409         if(!this.tabs){
30410             this.el.addClass("x-dlg-auto-tabs");
30411             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30412             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30413         }
30414         return this.tabs;
30415     },
30416
30417     /**
30418      * Adds a button to the footer section of the dialog.
30419      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30420      * object or a valid Roo.DomHelper element config
30421      * @param {Function} handler The function called when the button is clicked
30422      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30423      * @return {Roo.Button} The new button
30424      */
30425     addButton : function(config, handler, scope){
30426         var dh = Roo.DomHelper;
30427         if(!this.footer){
30428             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30429         }
30430         if(!this.btnContainer){
30431             var tb = this.footer.createChild({
30432
30433                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30434                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30435             }, null, true);
30436             this.btnContainer = tb.firstChild.firstChild.firstChild;
30437         }
30438         var bconfig = {
30439             handler: handler,
30440             scope: scope,
30441             minWidth: this.minButtonWidth,
30442             hideParent:true
30443         };
30444         if(typeof config == "string"){
30445             bconfig.text = config;
30446         }else{
30447             if(config.tag){
30448                 bconfig.dhconfig = config;
30449             }else{
30450                 Roo.apply(bconfig, config);
30451             }
30452         }
30453         var fc = false;
30454         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30455             bconfig.position = Math.max(0, bconfig.position);
30456             fc = this.btnContainer.childNodes[bconfig.position];
30457         }
30458          
30459         var btn = new Roo.Button(
30460             fc ? 
30461                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30462                 : this.btnContainer.appendChild(document.createElement("td")),
30463             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30464             bconfig
30465         );
30466         this.syncBodyHeight();
30467         if(!this.buttons){
30468             /**
30469              * Array of all the buttons that have been added to this dialog via addButton
30470              * @type Array
30471              */
30472             this.buttons = [];
30473         }
30474         this.buttons.push(btn);
30475         return btn;
30476     },
30477
30478     /**
30479      * Sets the default button to be focused when the dialog is displayed.
30480      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30481      * @return {Roo.BasicDialog} this
30482      */
30483     setDefaultButton : function(btn){
30484         this.defaultButton = btn;
30485         return this;
30486     },
30487
30488     // private
30489     getHeaderFooterHeight : function(safe){
30490         var height = 0;
30491         if(this.header){
30492            height += this.header.getHeight();
30493         }
30494         if(this.footer){
30495            var fm = this.footer.getMargins();
30496             height += (this.footer.getHeight()+fm.top+fm.bottom);
30497         }
30498         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30499         height += this.centerBg.getPadding("tb");
30500         return height;
30501     },
30502
30503     // private
30504     syncBodyHeight : function()
30505     {
30506         var bd = this.body, // the text
30507             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30508             bw = this.bwrap;
30509         var height = this.size.height - this.getHeaderFooterHeight(false);
30510         bd.setHeight(height-bd.getMargins("tb"));
30511         var hh = this.header.getHeight();
30512         var h = this.size.height-hh;
30513         cb.setHeight(h);
30514         
30515         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30516         bw.setHeight(h-cb.getPadding("tb"));
30517         
30518         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30519         bd.setWidth(bw.getWidth(true));
30520         if(this.tabs){
30521             this.tabs.syncHeight();
30522             if(Roo.isIE){
30523                 this.tabs.el.repaint();
30524             }
30525         }
30526     },
30527
30528     /**
30529      * Restores the previous state of the dialog if Roo.state is configured.
30530      * @return {Roo.BasicDialog} this
30531      */
30532     restoreState : function(){
30533         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30534         if(box && box.width){
30535             this.xy = [box.x, box.y];
30536             this.resizeTo(box.width, box.height);
30537         }
30538         return this;
30539     },
30540
30541     // private
30542     beforeShow : function(){
30543         this.expand();
30544         if(this.fixedcenter){
30545             this.xy = this.el.getCenterXY(true);
30546         }
30547         if(this.modal){
30548             Roo.get(document.body).addClass("x-body-masked");
30549             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30550             this.mask.show();
30551         }
30552         this.constrainXY();
30553     },
30554
30555     // private
30556     animShow : function(){
30557         var b = Roo.get(this.animateTarget).getBox();
30558         this.proxy.setSize(b.width, b.height);
30559         this.proxy.setLocation(b.x, b.y);
30560         this.proxy.show();
30561         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30562                     true, .35, this.showEl.createDelegate(this));
30563     },
30564
30565     /**
30566      * Shows the dialog.
30567      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30568      * @return {Roo.BasicDialog} this
30569      */
30570     show : function(animateTarget){
30571         if (this.fireEvent("beforeshow", this) === false){
30572             return;
30573         }
30574         if(this.syncHeightBeforeShow){
30575             this.syncBodyHeight();
30576         }else if(this.firstShow){
30577             this.firstShow = false;
30578             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30579         }
30580         this.animateTarget = animateTarget || this.animateTarget;
30581         if(!this.el.isVisible()){
30582             this.beforeShow();
30583             if(this.animateTarget && Roo.get(this.animateTarget)){
30584                 this.animShow();
30585             }else{
30586                 this.showEl();
30587             }
30588         }
30589         return this;
30590     },
30591
30592     // private
30593     showEl : function(){
30594         this.proxy.hide();
30595         this.el.setXY(this.xy);
30596         this.el.show();
30597         this.adjustAssets(true);
30598         this.toFront();
30599         this.focus();
30600         // IE peekaboo bug - fix found by Dave Fenwick
30601         if(Roo.isIE){
30602             this.el.repaint();
30603         }
30604         this.fireEvent("show", this);
30605     },
30606
30607     /**
30608      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30609      * dialog itself will receive focus.
30610      */
30611     focus : function(){
30612         if(this.defaultButton){
30613             this.defaultButton.focus();
30614         }else{
30615             this.focusEl.focus();
30616         }
30617     },
30618
30619     // private
30620     constrainXY : function(){
30621         if(this.constraintoviewport !== false){
30622             if(!this.viewSize){
30623                 if(this.container){
30624                     var s = this.container.getSize();
30625                     this.viewSize = [s.width, s.height];
30626                 }else{
30627                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30628                 }
30629             }
30630             var s = Roo.get(this.container||document).getScroll();
30631
30632             var x = this.xy[0], y = this.xy[1];
30633             var w = this.size.width, h = this.size.height;
30634             var vw = this.viewSize[0], vh = this.viewSize[1];
30635             // only move it if it needs it
30636             var moved = false;
30637             // first validate right/bottom
30638             if(x + w > vw+s.left){
30639                 x = vw - w;
30640                 moved = true;
30641             }
30642             if(y + h > vh+s.top){
30643                 y = vh - h;
30644                 moved = true;
30645             }
30646             // then make sure top/left isn't negative
30647             if(x < s.left){
30648                 x = s.left;
30649                 moved = true;
30650             }
30651             if(y < s.top){
30652                 y = s.top;
30653                 moved = true;
30654             }
30655             if(moved){
30656                 // cache xy
30657                 this.xy = [x, y];
30658                 if(this.isVisible()){
30659                     this.el.setLocation(x, y);
30660                     this.adjustAssets();
30661                 }
30662             }
30663         }
30664     },
30665
30666     // private
30667     onDrag : function(){
30668         if(!this.proxyDrag){
30669             this.xy = this.el.getXY();
30670             this.adjustAssets();
30671         }
30672     },
30673
30674     // private
30675     adjustAssets : function(doShow){
30676         var x = this.xy[0], y = this.xy[1];
30677         var w = this.size.width, h = this.size.height;
30678         if(doShow === true){
30679             if(this.shadow){
30680                 this.shadow.show(this.el);
30681             }
30682             if(this.shim){
30683                 this.shim.show();
30684             }
30685         }
30686         if(this.shadow && this.shadow.isVisible()){
30687             this.shadow.show(this.el);
30688         }
30689         if(this.shim && this.shim.isVisible()){
30690             this.shim.setBounds(x, y, w, h);
30691         }
30692     },
30693
30694     // private
30695     adjustViewport : function(w, h){
30696         if(!w || !h){
30697             w = Roo.lib.Dom.getViewWidth();
30698             h = Roo.lib.Dom.getViewHeight();
30699         }
30700         // cache the size
30701         this.viewSize = [w, h];
30702         if(this.modal && this.mask.isVisible()){
30703             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30704             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30705         }
30706         if(this.isVisible()){
30707             this.constrainXY();
30708         }
30709     },
30710
30711     /**
30712      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30713      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30714      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30715      */
30716     destroy : function(removeEl){
30717         if(this.isVisible()){
30718             this.animateTarget = null;
30719             this.hide();
30720         }
30721         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30722         if(this.tabs){
30723             this.tabs.destroy(removeEl);
30724         }
30725         Roo.destroy(
30726              this.shim,
30727              this.proxy,
30728              this.resizer,
30729              this.close,
30730              this.mask
30731         );
30732         if(this.dd){
30733             this.dd.unreg();
30734         }
30735         if(this.buttons){
30736            for(var i = 0, len = this.buttons.length; i < len; i++){
30737                this.buttons[i].destroy();
30738            }
30739         }
30740         this.el.removeAllListeners();
30741         if(removeEl === true){
30742             this.el.update("");
30743             this.el.remove();
30744         }
30745         Roo.DialogManager.unregister(this);
30746     },
30747
30748     // private
30749     startMove : function(){
30750         if(this.proxyDrag){
30751             this.proxy.show();
30752         }
30753         if(this.constraintoviewport !== false){
30754             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30755         }
30756     },
30757
30758     // private
30759     endMove : function(){
30760         if(!this.proxyDrag){
30761             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30762         }else{
30763             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30764             this.proxy.hide();
30765         }
30766         this.refreshSize();
30767         this.adjustAssets();
30768         this.focus();
30769         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30770     },
30771
30772     /**
30773      * Brings this dialog to the front of any other visible dialogs
30774      * @return {Roo.BasicDialog} this
30775      */
30776     toFront : function(){
30777         Roo.DialogManager.bringToFront(this);
30778         return this;
30779     },
30780
30781     /**
30782      * Sends this dialog to the back (under) of any other visible dialogs
30783      * @return {Roo.BasicDialog} this
30784      */
30785     toBack : function(){
30786         Roo.DialogManager.sendToBack(this);
30787         return this;
30788     },
30789
30790     /**
30791      * Centers this dialog in the viewport
30792      * @return {Roo.BasicDialog} this
30793      */
30794     center : function(){
30795         var xy = this.el.getCenterXY(true);
30796         this.moveTo(xy[0], xy[1]);
30797         return this;
30798     },
30799
30800     /**
30801      * Moves the dialog's top-left corner to the specified point
30802      * @param {Number} x
30803      * @param {Number} y
30804      * @return {Roo.BasicDialog} this
30805      */
30806     moveTo : function(x, y){
30807         this.xy = [x,y];
30808         if(this.isVisible()){
30809             this.el.setXY(this.xy);
30810             this.adjustAssets();
30811         }
30812         return this;
30813     },
30814
30815     /**
30816      * Aligns the dialog to the specified element
30817      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30818      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30819      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30820      * @return {Roo.BasicDialog} this
30821      */
30822     alignTo : function(element, position, offsets){
30823         this.xy = this.el.getAlignToXY(element, position, offsets);
30824         if(this.isVisible()){
30825             this.el.setXY(this.xy);
30826             this.adjustAssets();
30827         }
30828         return this;
30829     },
30830
30831     /**
30832      * Anchors an element to another element and realigns it when the window is resized.
30833      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30834      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30835      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30836      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30837      * is a number, it is used as the buffer delay (defaults to 50ms).
30838      * @return {Roo.BasicDialog} this
30839      */
30840     anchorTo : function(el, alignment, offsets, monitorScroll){
30841         var action = function(){
30842             this.alignTo(el, alignment, offsets);
30843         };
30844         Roo.EventManager.onWindowResize(action, this);
30845         var tm = typeof monitorScroll;
30846         if(tm != 'undefined'){
30847             Roo.EventManager.on(window, 'scroll', action, this,
30848                 {buffer: tm == 'number' ? monitorScroll : 50});
30849         }
30850         action.call(this);
30851         return this;
30852     },
30853
30854     /**
30855      * Returns true if the dialog is visible
30856      * @return {Boolean}
30857      */
30858     isVisible : function(){
30859         return this.el.isVisible();
30860     },
30861
30862     // private
30863     animHide : function(callback){
30864         var b = Roo.get(this.animateTarget).getBox();
30865         this.proxy.show();
30866         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30867         this.el.hide();
30868         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30869                     this.hideEl.createDelegate(this, [callback]));
30870     },
30871
30872     /**
30873      * Hides the dialog.
30874      * @param {Function} callback (optional) Function to call when the dialog is hidden
30875      * @return {Roo.BasicDialog} this
30876      */
30877     hide : function(callback){
30878         if (this.fireEvent("beforehide", this) === false){
30879             return;
30880         }
30881         if(this.shadow){
30882             this.shadow.hide();
30883         }
30884         if(this.shim) {
30885           this.shim.hide();
30886         }
30887         // sometimes animateTarget seems to get set.. causing problems...
30888         // this just double checks..
30889         if(this.animateTarget && Roo.get(this.animateTarget)) {
30890            this.animHide(callback);
30891         }else{
30892             this.el.hide();
30893             this.hideEl(callback);
30894         }
30895         return this;
30896     },
30897
30898     // private
30899     hideEl : function(callback){
30900         this.proxy.hide();
30901         if(this.modal){
30902             this.mask.hide();
30903             Roo.get(document.body).removeClass("x-body-masked");
30904         }
30905         this.fireEvent("hide", this);
30906         if(typeof callback == "function"){
30907             callback();
30908         }
30909     },
30910
30911     // private
30912     hideAction : function(){
30913         this.setLeft("-10000px");
30914         this.setTop("-10000px");
30915         this.setStyle("visibility", "hidden");
30916     },
30917
30918     // private
30919     refreshSize : function(){
30920         this.size = this.el.getSize();
30921         this.xy = this.el.getXY();
30922         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30923     },
30924
30925     // private
30926     // z-index is managed by the DialogManager and may be overwritten at any time
30927     setZIndex : function(index){
30928         if(this.modal){
30929             this.mask.setStyle("z-index", index);
30930         }
30931         if(this.shim){
30932             this.shim.setStyle("z-index", ++index);
30933         }
30934         if(this.shadow){
30935             this.shadow.setZIndex(++index);
30936         }
30937         this.el.setStyle("z-index", ++index);
30938         if(this.proxy){
30939             this.proxy.setStyle("z-index", ++index);
30940         }
30941         if(this.resizer){
30942             this.resizer.proxy.setStyle("z-index", ++index);
30943         }
30944
30945         this.lastZIndex = index;
30946     },
30947
30948     /**
30949      * Returns the element for this dialog
30950      * @return {Roo.Element} The underlying dialog Element
30951      */
30952     getEl : function(){
30953         return this.el;
30954     }
30955 });
30956
30957 /**
30958  * @class Roo.DialogManager
30959  * Provides global access to BasicDialogs that have been created and
30960  * support for z-indexing (layering) multiple open dialogs.
30961  */
30962 Roo.DialogManager = function(){
30963     var list = {};
30964     var accessList = [];
30965     var front = null;
30966
30967     // private
30968     var sortDialogs = function(d1, d2){
30969         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30970     };
30971
30972     // private
30973     var orderDialogs = function(){
30974         accessList.sort(sortDialogs);
30975         var seed = Roo.DialogManager.zseed;
30976         for(var i = 0, len = accessList.length; i < len; i++){
30977             var dlg = accessList[i];
30978             if(dlg){
30979                 dlg.setZIndex(seed + (i*10));
30980             }
30981         }
30982     };
30983
30984     return {
30985         /**
30986          * The starting z-index for BasicDialogs (defaults to 9000)
30987          * @type Number The z-index value
30988          */
30989         zseed : 9000,
30990
30991         // private
30992         register : function(dlg){
30993             list[dlg.id] = dlg;
30994             accessList.push(dlg);
30995         },
30996
30997         // private
30998         unregister : function(dlg){
30999             delete list[dlg.id];
31000             var i=0;
31001             var len=0;
31002             if(!accessList.indexOf){
31003                 for(  i = 0, len = accessList.length; i < len; i++){
31004                     if(accessList[i] == dlg){
31005                         accessList.splice(i, 1);
31006                         return;
31007                     }
31008                 }
31009             }else{
31010                  i = accessList.indexOf(dlg);
31011                 if(i != -1){
31012                     accessList.splice(i, 1);
31013                 }
31014             }
31015         },
31016
31017         /**
31018          * Gets a registered dialog by id
31019          * @param {String/Object} id The id of the dialog or a dialog
31020          * @return {Roo.BasicDialog} this
31021          */
31022         get : function(id){
31023             return typeof id == "object" ? id : list[id];
31024         },
31025
31026         /**
31027          * Brings the specified dialog to the front
31028          * @param {String/Object} dlg The id of the dialog or a dialog
31029          * @return {Roo.BasicDialog} this
31030          */
31031         bringToFront : function(dlg){
31032             dlg = this.get(dlg);
31033             if(dlg != front){
31034                 front = dlg;
31035                 dlg._lastAccess = new Date().getTime();
31036                 orderDialogs();
31037             }
31038             return dlg;
31039         },
31040
31041         /**
31042          * Sends the specified dialog to the back
31043          * @param {String/Object} dlg The id of the dialog or a dialog
31044          * @return {Roo.BasicDialog} this
31045          */
31046         sendToBack : function(dlg){
31047             dlg = this.get(dlg);
31048             dlg._lastAccess = -(new Date().getTime());
31049             orderDialogs();
31050             return dlg;
31051         },
31052
31053         /**
31054          * Hides all dialogs
31055          */
31056         hideAll : function(){
31057             for(var id in list){
31058                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31059                     list[id].hide();
31060                 }
31061             }
31062         }
31063     };
31064 }();
31065
31066 /**
31067  * @class Roo.LayoutDialog
31068  * @extends Roo.BasicDialog
31069  * Dialog which provides adjustments for working with a layout in a Dialog.
31070  * Add your necessary layout config options to the dialog's config.<br>
31071  * Example usage (including a nested layout):
31072  * <pre><code>
31073 if(!dialog){
31074     dialog = new Roo.LayoutDialog("download-dlg", {
31075         modal: true,
31076         width:600,
31077         height:450,
31078         shadow:true,
31079         minWidth:500,
31080         minHeight:350,
31081         autoTabs:true,
31082         proxyDrag:true,
31083         // layout config merges with the dialog config
31084         center:{
31085             tabPosition: "top",
31086             alwaysShowTabs: true
31087         }
31088     });
31089     dialog.addKeyListener(27, dialog.hide, dialog);
31090     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31091     dialog.addButton("Build It!", this.getDownload, this);
31092
31093     // we can even add nested layouts
31094     var innerLayout = new Roo.BorderLayout("dl-inner", {
31095         east: {
31096             initialSize: 200,
31097             autoScroll:true,
31098             split:true
31099         },
31100         center: {
31101             autoScroll:true
31102         }
31103     });
31104     innerLayout.beginUpdate();
31105     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31106     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31107     innerLayout.endUpdate(true);
31108
31109     var layout = dialog.getLayout();
31110     layout.beginUpdate();
31111     layout.add("center", new Roo.ContentPanel("standard-panel",
31112                         {title: "Download the Source", fitToFrame:true}));
31113     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31114                {title: "Build your own roo.js"}));
31115     layout.getRegion("center").showPanel(sp);
31116     layout.endUpdate();
31117 }
31118 </code></pre>
31119     * @constructor
31120     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31121     * @param {Object} config configuration options
31122   */
31123 Roo.LayoutDialog = function(el, cfg){
31124     
31125     var config=  cfg;
31126     if (typeof(cfg) == 'undefined') {
31127         config = Roo.apply({}, el);
31128         // not sure why we use documentElement here.. - it should always be body.
31129         // IE7 borks horribly if we use documentElement.
31130         // webkit also does not like documentElement - it creates a body element...
31131         el = Roo.get( document.body || document.documentElement ).createChild();
31132         //config.autoCreate = true;
31133     }
31134     
31135     
31136     config.autoTabs = false;
31137     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31138     this.body.setStyle({overflow:"hidden", position:"relative"});
31139     this.layout = new Roo.BorderLayout(this.body.dom, config);
31140     this.layout.monitorWindowResize = false;
31141     this.el.addClass("x-dlg-auto-layout");
31142     // fix case when center region overwrites center function
31143     this.center = Roo.BasicDialog.prototype.center;
31144     this.on("show", this.layout.layout, this.layout, true);
31145     if (config.items) {
31146         var xitems = config.items;
31147         delete config.items;
31148         Roo.each(xitems, this.addxtype, this);
31149     }
31150     
31151     
31152 };
31153 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31154     /**
31155      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31156      * @deprecated
31157      */
31158     endUpdate : function(){
31159         this.layout.endUpdate();
31160     },
31161
31162     /**
31163      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31164      *  @deprecated
31165      */
31166     beginUpdate : function(){
31167         this.layout.beginUpdate();
31168     },
31169
31170     /**
31171      * Get the BorderLayout for this dialog
31172      * @return {Roo.BorderLayout}
31173      */
31174     getLayout : function(){
31175         return this.layout;
31176     },
31177
31178     showEl : function(){
31179         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31180         if(Roo.isIE7){
31181             this.layout.layout();
31182         }
31183     },
31184
31185     // private
31186     // Use the syncHeightBeforeShow config option to control this automatically
31187     syncBodyHeight : function(){
31188         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31189         if(this.layout){this.layout.layout();}
31190     },
31191     
31192       /**
31193      * Add an xtype element (actually adds to the layout.)
31194      * @return {Object} xdata xtype object data.
31195      */
31196     
31197     addxtype : function(c) {
31198         return this.layout.addxtype(c);
31199     }
31200 });/*
31201  * Based on:
31202  * Ext JS Library 1.1.1
31203  * Copyright(c) 2006-2007, Ext JS, LLC.
31204  *
31205  * Originally Released Under LGPL - original licence link has changed is not relivant.
31206  *
31207  * Fork - LGPL
31208  * <script type="text/javascript">
31209  */
31210  
31211 /**
31212  * @class Roo.MessageBox
31213  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31214  * Example usage:
31215  *<pre><code>
31216 // Basic alert:
31217 Roo.Msg.alert('Status', 'Changes saved successfully.');
31218
31219 // Prompt for user data:
31220 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31221     if (btn == 'ok'){
31222         // process text value...
31223     }
31224 });
31225
31226 // Show a dialog using config options:
31227 Roo.Msg.show({
31228    title:'Save Changes?',
31229    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31230    buttons: Roo.Msg.YESNOCANCEL,
31231    fn: processResult,
31232    animEl: 'elId'
31233 });
31234 </code></pre>
31235  * @singleton
31236  */
31237 Roo.MessageBox = function(){
31238     var dlg, opt, mask, waitTimer;
31239     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31240     var buttons, activeTextEl, bwidth;
31241
31242     // private
31243     var handleButton = function(button){
31244         dlg.hide();
31245         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31246     };
31247
31248     // private
31249     var handleHide = function(){
31250         if(opt && opt.cls){
31251             dlg.el.removeClass(opt.cls);
31252         }
31253         if(waitTimer){
31254             Roo.TaskMgr.stop(waitTimer);
31255             waitTimer = null;
31256         }
31257     };
31258
31259     // private
31260     var updateButtons = function(b){
31261         var width = 0;
31262         if(!b){
31263             buttons["ok"].hide();
31264             buttons["cancel"].hide();
31265             buttons["yes"].hide();
31266             buttons["no"].hide();
31267             dlg.footer.dom.style.display = 'none';
31268             return width;
31269         }
31270         dlg.footer.dom.style.display = '';
31271         for(var k in buttons){
31272             if(typeof buttons[k] != "function"){
31273                 if(b[k]){
31274                     buttons[k].show();
31275                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31276                     width += buttons[k].el.getWidth()+15;
31277                 }else{
31278                     buttons[k].hide();
31279                 }
31280             }
31281         }
31282         return width;
31283     };
31284
31285     // private
31286     var handleEsc = function(d, k, e){
31287         if(opt && opt.closable !== false){
31288             dlg.hide();
31289         }
31290         if(e){
31291             e.stopEvent();
31292         }
31293     };
31294
31295     return {
31296         /**
31297          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31298          * @return {Roo.BasicDialog} The BasicDialog element
31299          */
31300         getDialog : function(){
31301            if(!dlg){
31302                 dlg = new Roo.BasicDialog("x-msg-box", {
31303                     autoCreate : true,
31304                     shadow: true,
31305                     draggable: true,
31306                     resizable:false,
31307                     constraintoviewport:false,
31308                     fixedcenter:true,
31309                     collapsible : false,
31310                     shim:true,
31311                     modal: true,
31312                     width:400, height:100,
31313                     buttonAlign:"center",
31314                     closeClick : function(){
31315                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31316                             handleButton("no");
31317                         }else{
31318                             handleButton("cancel");
31319                         }
31320                     }
31321                 });
31322                 dlg.on("hide", handleHide);
31323                 mask = dlg.mask;
31324                 dlg.addKeyListener(27, handleEsc);
31325                 buttons = {};
31326                 var bt = this.buttonText;
31327                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31328                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31329                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31330                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31331                 bodyEl = dlg.body.createChild({
31332
31333                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
31334                 });
31335                 msgEl = bodyEl.dom.firstChild;
31336                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31337                 textboxEl.enableDisplayMode();
31338                 textboxEl.addKeyListener([10,13], function(){
31339                     if(dlg.isVisible() && opt && opt.buttons){
31340                         if(opt.buttons.ok){
31341                             handleButton("ok");
31342                         }else if(opt.buttons.yes){
31343                             handleButton("yes");
31344                         }
31345                     }
31346                 });
31347                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31348                 textareaEl.enableDisplayMode();
31349                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31350                 progressEl.enableDisplayMode();
31351                 var pf = progressEl.dom.firstChild;
31352                 if (pf) {
31353                     pp = Roo.get(pf.firstChild);
31354                     pp.setHeight(pf.offsetHeight);
31355                 }
31356                 
31357             }
31358             return dlg;
31359         },
31360
31361         /**
31362          * Updates the message box body text
31363          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31364          * the XHTML-compliant non-breaking space character '&amp;#160;')
31365          * @return {Roo.MessageBox} This message box
31366          */
31367         updateText : function(text){
31368             if(!dlg.isVisible() && !opt.width){
31369                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31370             }
31371             msgEl.innerHTML = text || '&#160;';
31372       
31373             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31374             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31375             var w = Math.max(
31376                     Math.min(opt.width || cw , this.maxWidth), 
31377                     Math.max(opt.minWidth || this.minWidth, bwidth)
31378             );
31379             if(opt.prompt){
31380                 activeTextEl.setWidth(w);
31381             }
31382             if(dlg.isVisible()){
31383                 dlg.fixedcenter = false;
31384             }
31385             // to big, make it scroll. = But as usual stupid IE does not support
31386             // !important..
31387             
31388             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31389                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31390                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31391             } else {
31392                 bodyEl.dom.style.height = '';
31393                 bodyEl.dom.style.overflowY = '';
31394             }
31395             if (cw > w) {
31396                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31397             } else {
31398                 bodyEl.dom.style.overflowX = '';
31399             }
31400             
31401             dlg.setContentSize(w, bodyEl.getHeight());
31402             if(dlg.isVisible()){
31403                 dlg.fixedcenter = true;
31404             }
31405             return this;
31406         },
31407
31408         /**
31409          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31410          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31411          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31412          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31413          * @return {Roo.MessageBox} This message box
31414          */
31415         updateProgress : function(value, text){
31416             if(text){
31417                 this.updateText(text);
31418             }
31419             if (pp) { // weird bug on my firefox - for some reason this is not defined
31420                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31421             }
31422             return this;
31423         },        
31424
31425         /**
31426          * Returns true if the message box is currently displayed
31427          * @return {Boolean} True if the message box is visible, else false
31428          */
31429         isVisible : function(){
31430             return dlg && dlg.isVisible();  
31431         },
31432
31433         /**
31434          * Hides the message box if it is displayed
31435          */
31436         hide : function(){
31437             if(this.isVisible()){
31438                 dlg.hide();
31439             }  
31440         },
31441
31442         /**
31443          * Displays a new message box, or reinitializes an existing message box, based on the config options
31444          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31445          * The following config object properties are supported:
31446          * <pre>
31447 Property    Type             Description
31448 ----------  ---------------  ------------------------------------------------------------------------------------
31449 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31450                                    closes (defaults to undefined)
31451 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31452                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31453 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31454                                    progress and wait dialogs will ignore this property and always hide the
31455                                    close button as they can only be closed programmatically.
31456 cls               String           A custom CSS class to apply to the message box element
31457 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31458                                    displayed (defaults to 75)
31459 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31460                                    function will be btn (the name of the button that was clicked, if applicable,
31461                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31462                                    Progress and wait dialogs will ignore this option since they do not respond to
31463                                    user actions and can only be closed programmatically, so any required function
31464                                    should be called by the same code after it closes the dialog.
31465 icon              String           A CSS class that provides a background image to be used as an icon for
31466                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31467 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31468 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31469 modal             Boolean          False to allow user interaction with the page while the message box is
31470                                    displayed (defaults to true)
31471 msg               String           A string that will replace the existing message box body text (defaults
31472                                    to the XHTML-compliant non-breaking space character '&#160;')
31473 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31474 progress          Boolean          True to display a progress bar (defaults to false)
31475 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31476 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31477 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31478 title             String           The title text
31479 value             String           The string value to set into the active textbox element if displayed
31480 wait              Boolean          True to display a progress bar (defaults to false)
31481 width             Number           The width of the dialog in pixels
31482 </pre>
31483          *
31484          * Example usage:
31485          * <pre><code>
31486 Roo.Msg.show({
31487    title: 'Address',
31488    msg: 'Please enter your address:',
31489    width: 300,
31490    buttons: Roo.MessageBox.OKCANCEL,
31491    multiline: true,
31492    fn: saveAddress,
31493    animEl: 'addAddressBtn'
31494 });
31495 </code></pre>
31496          * @param {Object} config Configuration options
31497          * @return {Roo.MessageBox} This message box
31498          */
31499         show : function(options)
31500         {
31501             
31502             // this causes nightmares if you show one dialog after another
31503             // especially on callbacks..
31504              
31505             if(this.isVisible()){
31506                 
31507                 this.hide();
31508                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31509                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31510                 Roo.log("New Dialog Message:" +  options.msg )
31511                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31512                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31513                 
31514             }
31515             var d = this.getDialog();
31516             opt = options;
31517             d.setTitle(opt.title || "&#160;");
31518             d.close.setDisplayed(opt.closable !== false);
31519             activeTextEl = textboxEl;
31520             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31521             if(opt.prompt){
31522                 if(opt.multiline){
31523                     textboxEl.hide();
31524                     textareaEl.show();
31525                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31526                         opt.multiline : this.defaultTextHeight);
31527                     activeTextEl = textareaEl;
31528                 }else{
31529                     textboxEl.show();
31530                     textareaEl.hide();
31531                 }
31532             }else{
31533                 textboxEl.hide();
31534                 textareaEl.hide();
31535             }
31536             progressEl.setDisplayed(opt.progress === true);
31537             this.updateProgress(0);
31538             activeTextEl.dom.value = opt.value || "";
31539             if(opt.prompt){
31540                 dlg.setDefaultButton(activeTextEl);
31541             }else{
31542                 var bs = opt.buttons;
31543                 var db = null;
31544                 if(bs && bs.ok){
31545                     db = buttons["ok"];
31546                 }else if(bs && bs.yes){
31547                     db = buttons["yes"];
31548                 }
31549                 dlg.setDefaultButton(db);
31550             }
31551             bwidth = updateButtons(opt.buttons);
31552             this.updateText(opt.msg);
31553             if(opt.cls){
31554                 d.el.addClass(opt.cls);
31555             }
31556             d.proxyDrag = opt.proxyDrag === true;
31557             d.modal = opt.modal !== false;
31558             d.mask = opt.modal !== false ? mask : false;
31559             if(!d.isVisible()){
31560                 // force it to the end of the z-index stack so it gets a cursor in FF
31561                 document.body.appendChild(dlg.el.dom);
31562                 d.animateTarget = null;
31563                 d.show(options.animEl);
31564             }
31565             return this;
31566         },
31567
31568         /**
31569          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31570          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31571          * and closing the message box when the process is complete.
31572          * @param {String} title The title bar text
31573          * @param {String} msg The message box body text
31574          * @return {Roo.MessageBox} This message box
31575          */
31576         progress : function(title, msg){
31577             this.show({
31578                 title : title,
31579                 msg : msg,
31580                 buttons: false,
31581                 progress:true,
31582                 closable:false,
31583                 minWidth: this.minProgressWidth,
31584                 modal : true
31585             });
31586             return this;
31587         },
31588
31589         /**
31590          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31591          * If a callback function is passed it will be called after the user clicks the button, and the
31592          * id of the button that was clicked will be passed as the only parameter to the callback
31593          * (could also be the top-right close button).
31594          * @param {String} title The title bar text
31595          * @param {String} msg The message box body text
31596          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31597          * @param {Object} scope (optional) The scope of the callback function
31598          * @return {Roo.MessageBox} This message box
31599          */
31600         alert : function(title, msg, fn, scope){
31601             this.show({
31602                 title : title,
31603                 msg : msg,
31604                 buttons: this.OK,
31605                 fn: fn,
31606                 scope : scope,
31607                 modal : true
31608             });
31609             return this;
31610         },
31611
31612         /**
31613          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31614          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31615          * You are responsible for closing the message box when the process is complete.
31616          * @param {String} msg The message box body text
31617          * @param {String} title (optional) The title bar text
31618          * @return {Roo.MessageBox} This message box
31619          */
31620         wait : function(msg, title){
31621             this.show({
31622                 title : title,
31623                 msg : msg,
31624                 buttons: false,
31625                 closable:false,
31626                 progress:true,
31627                 modal:true,
31628                 width:300,
31629                 wait:true
31630             });
31631             waitTimer = Roo.TaskMgr.start({
31632                 run: function(i){
31633                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31634                 },
31635                 interval: 1000
31636             });
31637             return this;
31638         },
31639
31640         /**
31641          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31642          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31643          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31644          * @param {String} title The title bar text
31645          * @param {String} msg The message box body text
31646          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31647          * @param {Object} scope (optional) The scope of the callback function
31648          * @return {Roo.MessageBox} This message box
31649          */
31650         confirm : function(title, msg, fn, scope){
31651             this.show({
31652                 title : title,
31653                 msg : msg,
31654                 buttons: this.YESNO,
31655                 fn: fn,
31656                 scope : scope,
31657                 modal : true
31658             });
31659             return this;
31660         },
31661
31662         /**
31663          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31664          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31665          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31666          * (could also be the top-right close button) and the text that was entered will be passed as the two
31667          * parameters to the callback.
31668          * @param {String} title The title bar text
31669          * @param {String} msg The message box body text
31670          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31671          * @param {Object} scope (optional) The scope of the callback function
31672          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31673          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31674          * @return {Roo.MessageBox} This message box
31675          */
31676         prompt : function(title, msg, fn, scope, multiline){
31677             this.show({
31678                 title : title,
31679                 msg : msg,
31680                 buttons: this.OKCANCEL,
31681                 fn: fn,
31682                 minWidth:250,
31683                 scope : scope,
31684                 prompt:true,
31685                 multiline: multiline,
31686                 modal : true
31687             });
31688             return this;
31689         },
31690
31691         /**
31692          * Button config that displays a single OK button
31693          * @type Object
31694          */
31695         OK : {ok:true},
31696         /**
31697          * Button config that displays Yes and No buttons
31698          * @type Object
31699          */
31700         YESNO : {yes:true, no:true},
31701         /**
31702          * Button config that displays OK and Cancel buttons
31703          * @type Object
31704          */
31705         OKCANCEL : {ok:true, cancel:true},
31706         /**
31707          * Button config that displays Yes, No and Cancel buttons
31708          * @type Object
31709          */
31710         YESNOCANCEL : {yes:true, no:true, cancel:true},
31711
31712         /**
31713          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31714          * @type Number
31715          */
31716         defaultTextHeight : 75,
31717         /**
31718          * The maximum width in pixels of the message box (defaults to 600)
31719          * @type Number
31720          */
31721         maxWidth : 600,
31722         /**
31723          * The minimum width in pixels of the message box (defaults to 100)
31724          * @type Number
31725          */
31726         minWidth : 100,
31727         /**
31728          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31729          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31730          * @type Number
31731          */
31732         minProgressWidth : 250,
31733         /**
31734          * An object containing the default button text strings that can be overriden for localized language support.
31735          * Supported properties are: ok, cancel, yes and no.
31736          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31737          * @type Object
31738          */
31739         buttonText : {
31740             ok : "OK",
31741             cancel : "Cancel",
31742             yes : "Yes",
31743             no : "No"
31744         }
31745     };
31746 }();
31747
31748 /**
31749  * Shorthand for {@link Roo.MessageBox}
31750  */
31751 Roo.Msg = Roo.MessageBox;/*
31752  * Based on:
31753  * Ext JS Library 1.1.1
31754  * Copyright(c) 2006-2007, Ext JS, LLC.
31755  *
31756  * Originally Released Under LGPL - original licence link has changed is not relivant.
31757  *
31758  * Fork - LGPL
31759  * <script type="text/javascript">
31760  */
31761 /**
31762  * @class Roo.QuickTips
31763  * Provides attractive and customizable tooltips for any element.
31764  * @singleton
31765  */
31766 Roo.QuickTips = function(){
31767     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31768     var ce, bd, xy, dd;
31769     var visible = false, disabled = true, inited = false;
31770     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31771     
31772     var onOver = function(e){
31773         if(disabled){
31774             return;
31775         }
31776         var t = e.getTarget();
31777         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31778             return;
31779         }
31780         if(ce && t == ce.el){
31781             clearTimeout(hideProc);
31782             return;
31783         }
31784         if(t && tagEls[t.id]){
31785             tagEls[t.id].el = t;
31786             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31787             return;
31788         }
31789         var ttp, et = Roo.fly(t);
31790         var ns = cfg.namespace;
31791         if(tm.interceptTitles && t.title){
31792             ttp = t.title;
31793             t.qtip = ttp;
31794             t.removeAttribute("title");
31795             e.preventDefault();
31796         }else{
31797             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31798         }
31799         if(ttp){
31800             showProc = show.defer(tm.showDelay, tm, [{
31801                 el: t, 
31802                 text: ttp, 
31803                 width: et.getAttributeNS(ns, cfg.width),
31804                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31805                 title: et.getAttributeNS(ns, cfg.title),
31806                     cls: et.getAttributeNS(ns, cfg.cls)
31807             }]);
31808         }
31809     };
31810     
31811     var onOut = function(e){
31812         clearTimeout(showProc);
31813         var t = e.getTarget();
31814         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31815             hideProc = setTimeout(hide, tm.hideDelay);
31816         }
31817     };
31818     
31819     var onMove = function(e){
31820         if(disabled){
31821             return;
31822         }
31823         xy = e.getXY();
31824         xy[1] += 18;
31825         if(tm.trackMouse && ce){
31826             el.setXY(xy);
31827         }
31828     };
31829     
31830     var onDown = function(e){
31831         clearTimeout(showProc);
31832         clearTimeout(hideProc);
31833         if(!e.within(el)){
31834             if(tm.hideOnClick){
31835                 hide();
31836                 tm.disable();
31837                 tm.enable.defer(100, tm);
31838             }
31839         }
31840     };
31841     
31842     var getPad = function(){
31843         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31844     };
31845
31846     var show = function(o){
31847         if(disabled){
31848             return;
31849         }
31850         clearTimeout(dismissProc);
31851         ce = o;
31852         if(removeCls){ // in case manually hidden
31853             el.removeClass(removeCls);
31854             removeCls = null;
31855         }
31856         if(ce.cls){
31857             el.addClass(ce.cls);
31858             removeCls = ce.cls;
31859         }
31860         if(ce.title){
31861             tipTitle.update(ce.title);
31862             tipTitle.show();
31863         }else{
31864             tipTitle.update('');
31865             tipTitle.hide();
31866         }
31867         el.dom.style.width  = tm.maxWidth+'px';
31868         //tipBody.dom.style.width = '';
31869         tipBodyText.update(o.text);
31870         var p = getPad(), w = ce.width;
31871         if(!w){
31872             var td = tipBodyText.dom;
31873             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31874             if(aw > tm.maxWidth){
31875                 w = tm.maxWidth;
31876             }else if(aw < tm.minWidth){
31877                 w = tm.minWidth;
31878             }else{
31879                 w = aw;
31880             }
31881         }
31882         //tipBody.setWidth(w);
31883         el.setWidth(parseInt(w, 10) + p);
31884         if(ce.autoHide === false){
31885             close.setDisplayed(true);
31886             if(dd){
31887                 dd.unlock();
31888             }
31889         }else{
31890             close.setDisplayed(false);
31891             if(dd){
31892                 dd.lock();
31893             }
31894         }
31895         if(xy){
31896             el.avoidY = xy[1]-18;
31897             el.setXY(xy);
31898         }
31899         if(tm.animate){
31900             el.setOpacity(.1);
31901             el.setStyle("visibility", "visible");
31902             el.fadeIn({callback: afterShow});
31903         }else{
31904             afterShow();
31905         }
31906     };
31907     
31908     var afterShow = function(){
31909         if(ce){
31910             el.show();
31911             esc.enable();
31912             if(tm.autoDismiss && ce.autoHide !== false){
31913                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31914             }
31915         }
31916     };
31917     
31918     var hide = function(noanim){
31919         clearTimeout(dismissProc);
31920         clearTimeout(hideProc);
31921         ce = null;
31922         if(el.isVisible()){
31923             esc.disable();
31924             if(noanim !== true && tm.animate){
31925                 el.fadeOut({callback: afterHide});
31926             }else{
31927                 afterHide();
31928             } 
31929         }
31930     };
31931     
31932     var afterHide = function(){
31933         el.hide();
31934         if(removeCls){
31935             el.removeClass(removeCls);
31936             removeCls = null;
31937         }
31938     };
31939     
31940     return {
31941         /**
31942         * @cfg {Number} minWidth
31943         * The minimum width of the quick tip (defaults to 40)
31944         */
31945        minWidth : 40,
31946         /**
31947         * @cfg {Number} maxWidth
31948         * The maximum width of the quick tip (defaults to 300)
31949         */
31950        maxWidth : 300,
31951         /**
31952         * @cfg {Boolean} interceptTitles
31953         * True to automatically use the element's DOM title value if available (defaults to false)
31954         */
31955        interceptTitles : false,
31956         /**
31957         * @cfg {Boolean} trackMouse
31958         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31959         */
31960        trackMouse : false,
31961         /**
31962         * @cfg {Boolean} hideOnClick
31963         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31964         */
31965        hideOnClick : true,
31966         /**
31967         * @cfg {Number} showDelay
31968         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31969         */
31970        showDelay : 500,
31971         /**
31972         * @cfg {Number} hideDelay
31973         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31974         */
31975        hideDelay : 200,
31976         /**
31977         * @cfg {Boolean} autoHide
31978         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31979         * Used in conjunction with hideDelay.
31980         */
31981        autoHide : true,
31982         /**
31983         * @cfg {Boolean}
31984         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31985         * (defaults to true).  Used in conjunction with autoDismissDelay.
31986         */
31987        autoDismiss : true,
31988         /**
31989         * @cfg {Number}
31990         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31991         */
31992        autoDismissDelay : 5000,
31993        /**
31994         * @cfg {Boolean} animate
31995         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31996         */
31997        animate : false,
31998
31999        /**
32000         * @cfg {String} title
32001         * Title text to display (defaults to '').  This can be any valid HTML markup.
32002         */
32003         title: '',
32004        /**
32005         * @cfg {String} text
32006         * Body text to display (defaults to '').  This can be any valid HTML markup.
32007         */
32008         text : '',
32009        /**
32010         * @cfg {String} cls
32011         * A CSS class to apply to the base quick tip element (defaults to '').
32012         */
32013         cls : '',
32014        /**
32015         * @cfg {Number} width
32016         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32017         * minWidth or maxWidth.
32018         */
32019         width : null,
32020
32021     /**
32022      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32023      * or display QuickTips in a page.
32024      */
32025        init : function(){
32026           tm = Roo.QuickTips;
32027           cfg = tm.tagConfig;
32028           if(!inited){
32029               if(!Roo.isReady){ // allow calling of init() before onReady
32030                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32031                   return;
32032               }
32033               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32034               el.fxDefaults = {stopFx: true};
32035               // maximum custom styling
32036               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
32037               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
32038               tipTitle = el.child('h3');
32039               tipTitle.enableDisplayMode("block");
32040               tipBody = el.child('div.x-tip-bd');
32041               tipBodyText = el.child('div.x-tip-bd-inner');
32042               //bdLeft = el.child('div.x-tip-bd-left');
32043               //bdRight = el.child('div.x-tip-bd-right');
32044               close = el.child('div.x-tip-close');
32045               close.enableDisplayMode("block");
32046               close.on("click", hide);
32047               var d = Roo.get(document);
32048               d.on("mousedown", onDown);
32049               d.on("mouseover", onOver);
32050               d.on("mouseout", onOut);
32051               d.on("mousemove", onMove);
32052               esc = d.addKeyListener(27, hide);
32053               esc.disable();
32054               if(Roo.dd.DD){
32055                   dd = el.initDD("default", null, {
32056                       onDrag : function(){
32057                           el.sync();  
32058                       }
32059                   });
32060                   dd.setHandleElId(tipTitle.id);
32061                   dd.lock();
32062               }
32063               inited = true;
32064           }
32065           this.enable(); 
32066        },
32067
32068     /**
32069      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32070      * are supported:
32071      * <pre>
32072 Property    Type                   Description
32073 ----------  ---------------------  ------------------------------------------------------------------------
32074 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32075      * </ul>
32076      * @param {Object} config The config object
32077      */
32078        register : function(config){
32079            var cs = config instanceof Array ? config : arguments;
32080            for(var i = 0, len = cs.length; i < len; i++) {
32081                var c = cs[i];
32082                var target = c.target;
32083                if(target){
32084                    if(target instanceof Array){
32085                        for(var j = 0, jlen = target.length; j < jlen; j++){
32086                            tagEls[target[j]] = c;
32087                        }
32088                    }else{
32089                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32090                    }
32091                }
32092            }
32093        },
32094
32095     /**
32096      * Removes this quick tip from its element and destroys it.
32097      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32098      */
32099        unregister : function(el){
32100            delete tagEls[Roo.id(el)];
32101        },
32102
32103     /**
32104      * Enable this quick tip.
32105      */
32106        enable : function(){
32107            if(inited && disabled){
32108                locks.pop();
32109                if(locks.length < 1){
32110                    disabled = false;
32111                }
32112            }
32113        },
32114
32115     /**
32116      * Disable this quick tip.
32117      */
32118        disable : function(){
32119           disabled = true;
32120           clearTimeout(showProc);
32121           clearTimeout(hideProc);
32122           clearTimeout(dismissProc);
32123           if(ce){
32124               hide(true);
32125           }
32126           locks.push(1);
32127        },
32128
32129     /**
32130      * Returns true if the quick tip is enabled, else false.
32131      */
32132        isEnabled : function(){
32133             return !disabled;
32134        },
32135
32136         // private
32137        tagConfig : {
32138            namespace : "ext",
32139            attribute : "qtip",
32140            width : "width",
32141            target : "target",
32142            title : "qtitle",
32143            hide : "hide",
32144            cls : "qclass"
32145        }
32146    };
32147 }();
32148
32149 // backwards compat
32150 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32151  * Based on:
32152  * Ext JS Library 1.1.1
32153  * Copyright(c) 2006-2007, Ext JS, LLC.
32154  *
32155  * Originally Released Under LGPL - original licence link has changed is not relivant.
32156  *
32157  * Fork - LGPL
32158  * <script type="text/javascript">
32159  */
32160  
32161
32162 /**
32163  * @class Roo.tree.TreePanel
32164  * @extends Roo.data.Tree
32165
32166  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32167  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32168  * @cfg {Boolean} enableDD true to enable drag and drop
32169  * @cfg {Boolean} enableDrag true to enable just drag
32170  * @cfg {Boolean} enableDrop true to enable just drop
32171  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32172  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32173  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32174  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32175  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32176  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32177  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32178  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32179  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32180  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32181  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32182  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32183  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32184  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32185  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32186  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32187  * 
32188  * @constructor
32189  * @param {String/HTMLElement/Element} el The container element
32190  * @param {Object} config
32191  */
32192 Roo.tree.TreePanel = function(el, config){
32193     var root = false;
32194     var loader = false;
32195     if (config.root) {
32196         root = config.root;
32197         delete config.root;
32198     }
32199     if (config.loader) {
32200         loader = config.loader;
32201         delete config.loader;
32202     }
32203     
32204     Roo.apply(this, config);
32205     Roo.tree.TreePanel.superclass.constructor.call(this);
32206     this.el = Roo.get(el);
32207     this.el.addClass('x-tree');
32208     //console.log(root);
32209     if (root) {
32210         this.setRootNode( Roo.factory(root, Roo.tree));
32211     }
32212     if (loader) {
32213         this.loader = Roo.factory(loader, Roo.tree);
32214     }
32215    /**
32216     * Read-only. The id of the container element becomes this TreePanel's id.
32217     */
32218     this.id = this.el.id;
32219     this.addEvents({
32220         /**
32221         * @event beforeload
32222         * Fires before a node is loaded, return false to cancel
32223         * @param {Node} node The node being loaded
32224         */
32225         "beforeload" : true,
32226         /**
32227         * @event load
32228         * Fires when a node is loaded
32229         * @param {Node} node The node that was loaded
32230         */
32231         "load" : true,
32232         /**
32233         * @event textchange
32234         * Fires when the text for a node is changed
32235         * @param {Node} node The node
32236         * @param {String} text The new text
32237         * @param {String} oldText The old text
32238         */
32239         "textchange" : true,
32240         /**
32241         * @event beforeexpand
32242         * Fires before a node is expanded, return false to cancel.
32243         * @param {Node} node The node
32244         * @param {Boolean} deep
32245         * @param {Boolean} anim
32246         */
32247         "beforeexpand" : true,
32248         /**
32249         * @event beforecollapse
32250         * Fires before a node is collapsed, return false to cancel.
32251         * @param {Node} node The node
32252         * @param {Boolean} deep
32253         * @param {Boolean} anim
32254         */
32255         "beforecollapse" : true,
32256         /**
32257         * @event expand
32258         * Fires when a node is expanded
32259         * @param {Node} node The node
32260         */
32261         "expand" : true,
32262         /**
32263         * @event disabledchange
32264         * Fires when the disabled status of a node changes
32265         * @param {Node} node The node
32266         * @param {Boolean} disabled
32267         */
32268         "disabledchange" : true,
32269         /**
32270         * @event collapse
32271         * Fires when a node is collapsed
32272         * @param {Node} node The node
32273         */
32274         "collapse" : true,
32275         /**
32276         * @event beforeclick
32277         * Fires before click processing on a node. Return false to cancel the default action.
32278         * @param {Node} node The node
32279         * @param {Roo.EventObject} e The event object
32280         */
32281         "beforeclick":true,
32282         /**
32283         * @event checkchange
32284         * Fires when a node with a checkbox's checked property changes
32285         * @param {Node} this This node
32286         * @param {Boolean} checked
32287         */
32288         "checkchange":true,
32289         /**
32290         * @event click
32291         * Fires when a node is clicked
32292         * @param {Node} node The node
32293         * @param {Roo.EventObject} e The event object
32294         */
32295         "click":true,
32296         /**
32297         * @event dblclick
32298         * Fires when a node is double clicked
32299         * @param {Node} node The node
32300         * @param {Roo.EventObject} e The event object
32301         */
32302         "dblclick":true,
32303         /**
32304         * @event contextmenu
32305         * Fires when a node is right clicked
32306         * @param {Node} node The node
32307         * @param {Roo.EventObject} e The event object
32308         */
32309         "contextmenu":true,
32310         /**
32311         * @event beforechildrenrendered
32312         * Fires right before the child nodes for a node are rendered
32313         * @param {Node} node The node
32314         */
32315         "beforechildrenrendered":true,
32316         /**
32317         * @event startdrag
32318         * Fires when a node starts being dragged
32319         * @param {Roo.tree.TreePanel} this
32320         * @param {Roo.tree.TreeNode} node
32321         * @param {event} e The raw browser event
32322         */ 
32323        "startdrag" : true,
32324        /**
32325         * @event enddrag
32326         * Fires when a drag operation is complete
32327         * @param {Roo.tree.TreePanel} this
32328         * @param {Roo.tree.TreeNode} node
32329         * @param {event} e The raw browser event
32330         */
32331        "enddrag" : true,
32332        /**
32333         * @event dragdrop
32334         * Fires when a dragged node is dropped on a valid DD target
32335         * @param {Roo.tree.TreePanel} this
32336         * @param {Roo.tree.TreeNode} node
32337         * @param {DD} dd The dd it was dropped on
32338         * @param {event} e The raw browser event
32339         */
32340        "dragdrop" : true,
32341        /**
32342         * @event beforenodedrop
32343         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32344         * passed to handlers has the following properties:<br />
32345         * <ul style="padding:5px;padding-left:16px;">
32346         * <li>tree - The TreePanel</li>
32347         * <li>target - The node being targeted for the drop</li>
32348         * <li>data - The drag data from the drag source</li>
32349         * <li>point - The point of the drop - append, above or below</li>
32350         * <li>source - The drag source</li>
32351         * <li>rawEvent - Raw mouse event</li>
32352         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32353         * to be inserted by setting them on this object.</li>
32354         * <li>cancel - Set this to true to cancel the drop.</li>
32355         * </ul>
32356         * @param {Object} dropEvent
32357         */
32358        "beforenodedrop" : true,
32359        /**
32360         * @event nodedrop
32361         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32362         * passed to handlers has the following properties:<br />
32363         * <ul style="padding:5px;padding-left:16px;">
32364         * <li>tree - The TreePanel</li>
32365         * <li>target - The node being targeted for the drop</li>
32366         * <li>data - The drag data from the drag source</li>
32367         * <li>point - The point of the drop - append, above or below</li>
32368         * <li>source - The drag source</li>
32369         * <li>rawEvent - Raw mouse event</li>
32370         * <li>dropNode - Dropped node(s).</li>
32371         * </ul>
32372         * @param {Object} dropEvent
32373         */
32374        "nodedrop" : true,
32375         /**
32376         * @event nodedragover
32377         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32378         * passed to handlers has the following properties:<br />
32379         * <ul style="padding:5px;padding-left:16px;">
32380         * <li>tree - The TreePanel</li>
32381         * <li>target - The node being targeted for the drop</li>
32382         * <li>data - The drag data from the drag source</li>
32383         * <li>point - The point of the drop - append, above or below</li>
32384         * <li>source - The drag source</li>
32385         * <li>rawEvent - Raw mouse event</li>
32386         * <li>dropNode - Drop node(s) provided by the source.</li>
32387         * <li>cancel - Set this to true to signal drop not allowed.</li>
32388         * </ul>
32389         * @param {Object} dragOverEvent
32390         */
32391        "nodedragover" : true
32392         
32393     });
32394     if(this.singleExpand){
32395        this.on("beforeexpand", this.restrictExpand, this);
32396     }
32397     if (this.editor) {
32398         this.editor.tree = this;
32399         this.editor = Roo.factory(this.editor, Roo.tree);
32400     }
32401     
32402     if (this.selModel) {
32403         this.selModel = Roo.factory(this.selModel, Roo.tree);
32404     }
32405    
32406 };
32407 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32408     rootVisible : true,
32409     animate: Roo.enableFx,
32410     lines : true,
32411     enableDD : false,
32412     hlDrop : Roo.enableFx,
32413   
32414     renderer: false,
32415     
32416     rendererTip: false,
32417     // private
32418     restrictExpand : function(node){
32419         var p = node.parentNode;
32420         if(p){
32421             if(p.expandedChild && p.expandedChild.parentNode == p){
32422                 p.expandedChild.collapse();
32423             }
32424             p.expandedChild = node;
32425         }
32426     },
32427
32428     // private override
32429     setRootNode : function(node){
32430         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32431         if(!this.rootVisible){
32432             node.ui = new Roo.tree.RootTreeNodeUI(node);
32433         }
32434         return node;
32435     },
32436
32437     /**
32438      * Returns the container element for this TreePanel
32439      */
32440     getEl : function(){
32441         return this.el;
32442     },
32443
32444     /**
32445      * Returns the default TreeLoader for this TreePanel
32446      */
32447     getLoader : function(){
32448         return this.loader;
32449     },
32450
32451     /**
32452      * Expand all nodes
32453      */
32454     expandAll : function(){
32455         this.root.expand(true);
32456     },
32457
32458     /**
32459      * Collapse all nodes
32460      */
32461     collapseAll : function(){
32462         this.root.collapse(true);
32463     },
32464
32465     /**
32466      * Returns the selection model used by this TreePanel
32467      */
32468     getSelectionModel : function(){
32469         if(!this.selModel){
32470             this.selModel = new Roo.tree.DefaultSelectionModel();
32471         }
32472         return this.selModel;
32473     },
32474
32475     /**
32476      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32477      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32478      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32479      * @return {Array}
32480      */
32481     getChecked : function(a, startNode){
32482         startNode = startNode || this.root;
32483         var r = [];
32484         var f = function(){
32485             if(this.attributes.checked){
32486                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32487             }
32488         }
32489         startNode.cascade(f);
32490         return r;
32491     },
32492
32493     /**
32494      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32495      * @param {String} path
32496      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32497      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32498      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32499      */
32500     expandPath : function(path, attr, callback){
32501         attr = attr || "id";
32502         var keys = path.split(this.pathSeparator);
32503         var curNode = this.root;
32504         if(curNode.attributes[attr] != keys[1]){ // invalid root
32505             if(callback){
32506                 callback(false, null);
32507             }
32508             return;
32509         }
32510         var index = 1;
32511         var f = function(){
32512             if(++index == keys.length){
32513                 if(callback){
32514                     callback(true, curNode);
32515                 }
32516                 return;
32517             }
32518             var c = curNode.findChild(attr, keys[index]);
32519             if(!c){
32520                 if(callback){
32521                     callback(false, curNode);
32522                 }
32523                 return;
32524             }
32525             curNode = c;
32526             c.expand(false, false, f);
32527         };
32528         curNode.expand(false, false, f);
32529     },
32530
32531     /**
32532      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32533      * @param {String} path
32534      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32535      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32536      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32537      */
32538     selectPath : function(path, attr, callback){
32539         attr = attr || "id";
32540         var keys = path.split(this.pathSeparator);
32541         var v = keys.pop();
32542         if(keys.length > 0){
32543             var f = function(success, node){
32544                 if(success && node){
32545                     var n = node.findChild(attr, v);
32546                     if(n){
32547                         n.select();
32548                         if(callback){
32549                             callback(true, n);
32550                         }
32551                     }else if(callback){
32552                         callback(false, n);
32553                     }
32554                 }else{
32555                     if(callback){
32556                         callback(false, n);
32557                     }
32558                 }
32559             };
32560             this.expandPath(keys.join(this.pathSeparator), attr, f);
32561         }else{
32562             this.root.select();
32563             if(callback){
32564                 callback(true, this.root);
32565             }
32566         }
32567     },
32568
32569     getTreeEl : function(){
32570         return this.el;
32571     },
32572
32573     /**
32574      * Trigger rendering of this TreePanel
32575      */
32576     render : function(){
32577         if (this.innerCt) {
32578             return this; // stop it rendering more than once!!
32579         }
32580         
32581         this.innerCt = this.el.createChild({tag:"ul",
32582                cls:"x-tree-root-ct " +
32583                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32584
32585         if(this.containerScroll){
32586             Roo.dd.ScrollManager.register(this.el);
32587         }
32588         if((this.enableDD || this.enableDrop) && !this.dropZone){
32589            /**
32590             * The dropZone used by this tree if drop is enabled
32591             * @type Roo.tree.TreeDropZone
32592             */
32593              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32594                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32595            });
32596         }
32597         if((this.enableDD || this.enableDrag) && !this.dragZone){
32598            /**
32599             * The dragZone used by this tree if drag is enabled
32600             * @type Roo.tree.TreeDragZone
32601             */
32602             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32603                ddGroup: this.ddGroup || "TreeDD",
32604                scroll: this.ddScroll
32605            });
32606         }
32607         this.getSelectionModel().init(this);
32608         if (!this.root) {
32609             Roo.log("ROOT not set in tree");
32610             return this;
32611         }
32612         this.root.render();
32613         if(!this.rootVisible){
32614             this.root.renderChildren();
32615         }
32616         return this;
32617     }
32618 });/*
32619  * Based on:
32620  * Ext JS Library 1.1.1
32621  * Copyright(c) 2006-2007, Ext JS, LLC.
32622  *
32623  * Originally Released Under LGPL - original licence link has changed is not relivant.
32624  *
32625  * Fork - LGPL
32626  * <script type="text/javascript">
32627  */
32628  
32629
32630 /**
32631  * @class Roo.tree.DefaultSelectionModel
32632  * @extends Roo.util.Observable
32633  * The default single selection for a TreePanel.
32634  * @param {Object} cfg Configuration
32635  */
32636 Roo.tree.DefaultSelectionModel = function(cfg){
32637    this.selNode = null;
32638    
32639    
32640    
32641    this.addEvents({
32642        /**
32643         * @event selectionchange
32644         * Fires when the selected node changes
32645         * @param {DefaultSelectionModel} this
32646         * @param {TreeNode} node the new selection
32647         */
32648        "selectionchange" : true,
32649
32650        /**
32651         * @event beforeselect
32652         * Fires before the selected node changes, return false to cancel the change
32653         * @param {DefaultSelectionModel} this
32654         * @param {TreeNode} node the new selection
32655         * @param {TreeNode} node the old selection
32656         */
32657        "beforeselect" : true
32658    });
32659    
32660     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32661 };
32662
32663 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32664     init : function(tree){
32665         this.tree = tree;
32666         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32667         tree.on("click", this.onNodeClick, this);
32668     },
32669     
32670     onNodeClick : function(node, e){
32671         if (e.ctrlKey && this.selNode == node)  {
32672             this.unselect(node);
32673             return;
32674         }
32675         this.select(node);
32676     },
32677     
32678     /**
32679      * Select a node.
32680      * @param {TreeNode} node The node to select
32681      * @return {TreeNode} The selected node
32682      */
32683     select : function(node){
32684         var last = this.selNode;
32685         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32686             if(last){
32687                 last.ui.onSelectedChange(false);
32688             }
32689             this.selNode = node;
32690             node.ui.onSelectedChange(true);
32691             this.fireEvent("selectionchange", this, node, last);
32692         }
32693         return node;
32694     },
32695     
32696     /**
32697      * Deselect a node.
32698      * @param {TreeNode} node The node to unselect
32699      */
32700     unselect : function(node){
32701         if(this.selNode == node){
32702             this.clearSelections();
32703         }    
32704     },
32705     
32706     /**
32707      * Clear all selections
32708      */
32709     clearSelections : function(){
32710         var n = this.selNode;
32711         if(n){
32712             n.ui.onSelectedChange(false);
32713             this.selNode = null;
32714             this.fireEvent("selectionchange", this, null);
32715         }
32716         return n;
32717     },
32718     
32719     /**
32720      * Get the selected node
32721      * @return {TreeNode} The selected node
32722      */
32723     getSelectedNode : function(){
32724         return this.selNode;    
32725     },
32726     
32727     /**
32728      * Returns true if the node is selected
32729      * @param {TreeNode} node The node to check
32730      * @return {Boolean}
32731      */
32732     isSelected : function(node){
32733         return this.selNode == node;  
32734     },
32735
32736     /**
32737      * Selects the node above the selected node in the tree, intelligently walking the nodes
32738      * @return TreeNode The new selection
32739      */
32740     selectPrevious : function(){
32741         var s = this.selNode || this.lastSelNode;
32742         if(!s){
32743             return null;
32744         }
32745         var ps = s.previousSibling;
32746         if(ps){
32747             if(!ps.isExpanded() || ps.childNodes.length < 1){
32748                 return this.select(ps);
32749             } else{
32750                 var lc = ps.lastChild;
32751                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32752                     lc = lc.lastChild;
32753                 }
32754                 return this.select(lc);
32755             }
32756         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32757             return this.select(s.parentNode);
32758         }
32759         return null;
32760     },
32761
32762     /**
32763      * Selects the node above the selected node in the tree, intelligently walking the nodes
32764      * @return TreeNode The new selection
32765      */
32766     selectNext : function(){
32767         var s = this.selNode || this.lastSelNode;
32768         if(!s){
32769             return null;
32770         }
32771         if(s.firstChild && s.isExpanded()){
32772              return this.select(s.firstChild);
32773          }else if(s.nextSibling){
32774              return this.select(s.nextSibling);
32775          }else if(s.parentNode){
32776             var newS = null;
32777             s.parentNode.bubble(function(){
32778                 if(this.nextSibling){
32779                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32780                     return false;
32781                 }
32782             });
32783             return newS;
32784          }
32785         return null;
32786     },
32787
32788     onKeyDown : function(e){
32789         var s = this.selNode || this.lastSelNode;
32790         // undesirable, but required
32791         var sm = this;
32792         if(!s){
32793             return;
32794         }
32795         var k = e.getKey();
32796         switch(k){
32797              case e.DOWN:
32798                  e.stopEvent();
32799                  this.selectNext();
32800              break;
32801              case e.UP:
32802                  e.stopEvent();
32803                  this.selectPrevious();
32804              break;
32805              case e.RIGHT:
32806                  e.preventDefault();
32807                  if(s.hasChildNodes()){
32808                      if(!s.isExpanded()){
32809                          s.expand();
32810                      }else if(s.firstChild){
32811                          this.select(s.firstChild, e);
32812                      }
32813                  }
32814              break;
32815              case e.LEFT:
32816                  e.preventDefault();
32817                  if(s.hasChildNodes() && s.isExpanded()){
32818                      s.collapse();
32819                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32820                      this.select(s.parentNode, e);
32821                  }
32822              break;
32823         };
32824     }
32825 });
32826
32827 /**
32828  * @class Roo.tree.MultiSelectionModel
32829  * @extends Roo.util.Observable
32830  * Multi selection for a TreePanel.
32831  * @param {Object} cfg Configuration
32832  */
32833 Roo.tree.MultiSelectionModel = function(){
32834    this.selNodes = [];
32835    this.selMap = {};
32836    this.addEvents({
32837        /**
32838         * @event selectionchange
32839         * Fires when the selected nodes change
32840         * @param {MultiSelectionModel} this
32841         * @param {Array} nodes Array of the selected nodes
32842         */
32843        "selectionchange" : true
32844    });
32845    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32846    
32847 };
32848
32849 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32850     init : function(tree){
32851         this.tree = tree;
32852         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32853         tree.on("click", this.onNodeClick, this);
32854     },
32855     
32856     onNodeClick : function(node, e){
32857         this.select(node, e, e.ctrlKey);
32858     },
32859     
32860     /**
32861      * Select a node.
32862      * @param {TreeNode} node The node to select
32863      * @param {EventObject} e (optional) An event associated with the selection
32864      * @param {Boolean} keepExisting True to retain existing selections
32865      * @return {TreeNode} The selected node
32866      */
32867     select : function(node, e, keepExisting){
32868         if(keepExisting !== true){
32869             this.clearSelections(true);
32870         }
32871         if(this.isSelected(node)){
32872             this.lastSelNode = node;
32873             return node;
32874         }
32875         this.selNodes.push(node);
32876         this.selMap[node.id] = node;
32877         this.lastSelNode = node;
32878         node.ui.onSelectedChange(true);
32879         this.fireEvent("selectionchange", this, this.selNodes);
32880         return node;
32881     },
32882     
32883     /**
32884      * Deselect a node.
32885      * @param {TreeNode} node The node to unselect
32886      */
32887     unselect : function(node){
32888         if(this.selMap[node.id]){
32889             node.ui.onSelectedChange(false);
32890             var sn = this.selNodes;
32891             var index = -1;
32892             if(sn.indexOf){
32893                 index = sn.indexOf(node);
32894             }else{
32895                 for(var i = 0, len = sn.length; i < len; i++){
32896                     if(sn[i] == node){
32897                         index = i;
32898                         break;
32899                     }
32900                 }
32901             }
32902             if(index != -1){
32903                 this.selNodes.splice(index, 1);
32904             }
32905             delete this.selMap[node.id];
32906             this.fireEvent("selectionchange", this, this.selNodes);
32907         }
32908     },
32909     
32910     /**
32911      * Clear all selections
32912      */
32913     clearSelections : function(suppressEvent){
32914         var sn = this.selNodes;
32915         if(sn.length > 0){
32916             for(var i = 0, len = sn.length; i < len; i++){
32917                 sn[i].ui.onSelectedChange(false);
32918             }
32919             this.selNodes = [];
32920             this.selMap = {};
32921             if(suppressEvent !== true){
32922                 this.fireEvent("selectionchange", this, this.selNodes);
32923             }
32924         }
32925     },
32926     
32927     /**
32928      * Returns true if the node is selected
32929      * @param {TreeNode} node The node to check
32930      * @return {Boolean}
32931      */
32932     isSelected : function(node){
32933         return this.selMap[node.id] ? true : false;  
32934     },
32935     
32936     /**
32937      * Returns an array of the selected nodes
32938      * @return {Array}
32939      */
32940     getSelectedNodes : function(){
32941         return this.selNodes;    
32942     },
32943
32944     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32945
32946     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32947
32948     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32949 });/*
32950  * Based on:
32951  * Ext JS Library 1.1.1
32952  * Copyright(c) 2006-2007, Ext JS, LLC.
32953  *
32954  * Originally Released Under LGPL - original licence link has changed is not relivant.
32955  *
32956  * Fork - LGPL
32957  * <script type="text/javascript">
32958  */
32959  
32960 /**
32961  * @class Roo.tree.TreeNode
32962  * @extends Roo.data.Node
32963  * @cfg {String} text The text for this node
32964  * @cfg {Boolean} expanded true to start the node expanded
32965  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32966  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32967  * @cfg {Boolean} disabled true to start the node disabled
32968  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32969  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32970  * @cfg {String} cls A css class to be added to the node
32971  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32972  * @cfg {String} href URL of the link used for the node (defaults to #)
32973  * @cfg {String} hrefTarget target frame for the link
32974  * @cfg {String} qtip An Ext QuickTip for the node
32975  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32976  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32977  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32978  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32979  * (defaults to undefined with no checkbox rendered)
32980  * @constructor
32981  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32982  */
32983 Roo.tree.TreeNode = function(attributes){
32984     attributes = attributes || {};
32985     if(typeof attributes == "string"){
32986         attributes = {text: attributes};
32987     }
32988     this.childrenRendered = false;
32989     this.rendered = false;
32990     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32991     this.expanded = attributes.expanded === true;
32992     this.isTarget = attributes.isTarget !== false;
32993     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32994     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32995
32996     /**
32997      * Read-only. The text for this node. To change it use setText().
32998      * @type String
32999      */
33000     this.text = attributes.text;
33001     /**
33002      * True if this node is disabled.
33003      * @type Boolean
33004      */
33005     this.disabled = attributes.disabled === true;
33006
33007     this.addEvents({
33008         /**
33009         * @event textchange
33010         * Fires when the text for this node is changed
33011         * @param {Node} this This node
33012         * @param {String} text The new text
33013         * @param {String} oldText The old text
33014         */
33015         "textchange" : true,
33016         /**
33017         * @event beforeexpand
33018         * Fires before this node is expanded, return false to cancel.
33019         * @param {Node} this This node
33020         * @param {Boolean} deep
33021         * @param {Boolean} anim
33022         */
33023         "beforeexpand" : true,
33024         /**
33025         * @event beforecollapse
33026         * Fires before this node is collapsed, return false to cancel.
33027         * @param {Node} this This node
33028         * @param {Boolean} deep
33029         * @param {Boolean} anim
33030         */
33031         "beforecollapse" : true,
33032         /**
33033         * @event expand
33034         * Fires when this node is expanded
33035         * @param {Node} this This node
33036         */
33037         "expand" : true,
33038         /**
33039         * @event disabledchange
33040         * Fires when the disabled status of this node changes
33041         * @param {Node} this This node
33042         * @param {Boolean} disabled
33043         */
33044         "disabledchange" : true,
33045         /**
33046         * @event collapse
33047         * Fires when this node is collapsed
33048         * @param {Node} this This node
33049         */
33050         "collapse" : true,
33051         /**
33052         * @event beforeclick
33053         * Fires before click processing. Return false to cancel the default action.
33054         * @param {Node} this This node
33055         * @param {Roo.EventObject} e The event object
33056         */
33057         "beforeclick":true,
33058         /**
33059         * @event checkchange
33060         * Fires when a node with a checkbox's checked property changes
33061         * @param {Node} this This node
33062         * @param {Boolean} checked
33063         */
33064         "checkchange":true,
33065         /**
33066         * @event click
33067         * Fires when this node is clicked
33068         * @param {Node} this This node
33069         * @param {Roo.EventObject} e The event object
33070         */
33071         "click":true,
33072         /**
33073         * @event dblclick
33074         * Fires when this node is double clicked
33075         * @param {Node} this This node
33076         * @param {Roo.EventObject} e The event object
33077         */
33078         "dblclick":true,
33079         /**
33080         * @event contextmenu
33081         * Fires when this node is right clicked
33082         * @param {Node} this This node
33083         * @param {Roo.EventObject} e The event object
33084         */
33085         "contextmenu":true,
33086         /**
33087         * @event beforechildrenrendered
33088         * Fires right before the child nodes for this node are rendered
33089         * @param {Node} this This node
33090         */
33091         "beforechildrenrendered":true
33092     });
33093
33094     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33095
33096     /**
33097      * Read-only. The UI for this node
33098      * @type TreeNodeUI
33099      */
33100     this.ui = new uiClass(this);
33101     
33102     // finally support items[]
33103     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33104         return;
33105     }
33106     
33107     
33108     Roo.each(this.attributes.items, function(c) {
33109         this.appendChild(Roo.factory(c,Roo.Tree));
33110     }, this);
33111     delete this.attributes.items;
33112     
33113     
33114     
33115 };
33116 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33117     preventHScroll: true,
33118     /**
33119      * Returns true if this node is expanded
33120      * @return {Boolean}
33121      */
33122     isExpanded : function(){
33123         return this.expanded;
33124     },
33125
33126     /**
33127      * Returns the UI object for this node
33128      * @return {TreeNodeUI}
33129      */
33130     getUI : function(){
33131         return this.ui;
33132     },
33133
33134     // private override
33135     setFirstChild : function(node){
33136         var of = this.firstChild;
33137         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33138         if(this.childrenRendered && of && node != of){
33139             of.renderIndent(true, true);
33140         }
33141         if(this.rendered){
33142             this.renderIndent(true, true);
33143         }
33144     },
33145
33146     // private override
33147     setLastChild : function(node){
33148         var ol = this.lastChild;
33149         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33150         if(this.childrenRendered && ol && node != ol){
33151             ol.renderIndent(true, true);
33152         }
33153         if(this.rendered){
33154             this.renderIndent(true, true);
33155         }
33156     },
33157
33158     // these methods are overridden to provide lazy rendering support
33159     // private override
33160     appendChild : function()
33161     {
33162         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33163         if(node && this.childrenRendered){
33164             node.render();
33165         }
33166         this.ui.updateExpandIcon();
33167         return node;
33168     },
33169
33170     // private override
33171     removeChild : function(node){
33172         this.ownerTree.getSelectionModel().unselect(node);
33173         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33174         // if it's been rendered remove dom node
33175         if(this.childrenRendered){
33176             node.ui.remove();
33177         }
33178         if(this.childNodes.length < 1){
33179             this.collapse(false, false);
33180         }else{
33181             this.ui.updateExpandIcon();
33182         }
33183         if(!this.firstChild) {
33184             this.childrenRendered = false;
33185         }
33186         return node;
33187     },
33188
33189     // private override
33190     insertBefore : function(node, refNode){
33191         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33192         if(newNode && refNode && this.childrenRendered){
33193             node.render();
33194         }
33195         this.ui.updateExpandIcon();
33196         return newNode;
33197     },
33198
33199     /**
33200      * Sets the text for this node
33201      * @param {String} text
33202      */
33203     setText : function(text){
33204         var oldText = this.text;
33205         this.text = text;
33206         this.attributes.text = text;
33207         if(this.rendered){ // event without subscribing
33208             this.ui.onTextChange(this, text, oldText);
33209         }
33210         this.fireEvent("textchange", this, text, oldText);
33211     },
33212
33213     /**
33214      * Triggers selection of this node
33215      */
33216     select : function(){
33217         this.getOwnerTree().getSelectionModel().select(this);
33218     },
33219
33220     /**
33221      * Triggers deselection of this node
33222      */
33223     unselect : function(){
33224         this.getOwnerTree().getSelectionModel().unselect(this);
33225     },
33226
33227     /**
33228      * Returns true if this node is selected
33229      * @return {Boolean}
33230      */
33231     isSelected : function(){
33232         return this.getOwnerTree().getSelectionModel().isSelected(this);
33233     },
33234
33235     /**
33236      * Expand this node.
33237      * @param {Boolean} deep (optional) True to expand all children as well
33238      * @param {Boolean} anim (optional) false to cancel the default animation
33239      * @param {Function} callback (optional) A callback to be called when
33240      * expanding this node completes (does not wait for deep expand to complete).
33241      * Called with 1 parameter, this node.
33242      */
33243     expand : function(deep, anim, callback){
33244         if(!this.expanded){
33245             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33246                 return;
33247             }
33248             if(!this.childrenRendered){
33249                 this.renderChildren();
33250             }
33251             this.expanded = true;
33252             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33253                 this.ui.animExpand(function(){
33254                     this.fireEvent("expand", this);
33255                     if(typeof callback == "function"){
33256                         callback(this);
33257                     }
33258                     if(deep === true){
33259                         this.expandChildNodes(true);
33260                     }
33261                 }.createDelegate(this));
33262                 return;
33263             }else{
33264                 this.ui.expand();
33265                 this.fireEvent("expand", this);
33266                 if(typeof callback == "function"){
33267                     callback(this);
33268                 }
33269             }
33270         }else{
33271            if(typeof callback == "function"){
33272                callback(this);
33273            }
33274         }
33275         if(deep === true){
33276             this.expandChildNodes(true);
33277         }
33278     },
33279
33280     isHiddenRoot : function(){
33281         return this.isRoot && !this.getOwnerTree().rootVisible;
33282     },
33283
33284     /**
33285      * Collapse this node.
33286      * @param {Boolean} deep (optional) True to collapse all children as well
33287      * @param {Boolean} anim (optional) false to cancel the default animation
33288      */
33289     collapse : function(deep, anim){
33290         if(this.expanded && !this.isHiddenRoot()){
33291             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33292                 return;
33293             }
33294             this.expanded = false;
33295             if((this.getOwnerTree().animate && anim !== false) || anim){
33296                 this.ui.animCollapse(function(){
33297                     this.fireEvent("collapse", this);
33298                     if(deep === true){
33299                         this.collapseChildNodes(true);
33300                     }
33301                 }.createDelegate(this));
33302                 return;
33303             }else{
33304                 this.ui.collapse();
33305                 this.fireEvent("collapse", this);
33306             }
33307         }
33308         if(deep === true){
33309             var cs = this.childNodes;
33310             for(var i = 0, len = cs.length; i < len; i++) {
33311                 cs[i].collapse(true, false);
33312             }
33313         }
33314     },
33315
33316     // private
33317     delayedExpand : function(delay){
33318         if(!this.expandProcId){
33319             this.expandProcId = this.expand.defer(delay, this);
33320         }
33321     },
33322
33323     // private
33324     cancelExpand : function(){
33325         if(this.expandProcId){
33326             clearTimeout(this.expandProcId);
33327         }
33328         this.expandProcId = false;
33329     },
33330
33331     /**
33332      * Toggles expanded/collapsed state of the node
33333      */
33334     toggle : function(){
33335         if(this.expanded){
33336             this.collapse();
33337         }else{
33338             this.expand();
33339         }
33340     },
33341
33342     /**
33343      * Ensures all parent nodes are expanded
33344      */
33345     ensureVisible : function(callback){
33346         var tree = this.getOwnerTree();
33347         tree.expandPath(this.parentNode.getPath(), false, function(){
33348             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33349             Roo.callback(callback);
33350         }.createDelegate(this));
33351     },
33352
33353     /**
33354      * Expand all child nodes
33355      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33356      */
33357     expandChildNodes : function(deep){
33358         var cs = this.childNodes;
33359         for(var i = 0, len = cs.length; i < len; i++) {
33360                 cs[i].expand(deep);
33361         }
33362     },
33363
33364     /**
33365      * Collapse all child nodes
33366      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33367      */
33368     collapseChildNodes : function(deep){
33369         var cs = this.childNodes;
33370         for(var i = 0, len = cs.length; i < len; i++) {
33371                 cs[i].collapse(deep);
33372         }
33373     },
33374
33375     /**
33376      * Disables this node
33377      */
33378     disable : function(){
33379         this.disabled = true;
33380         this.unselect();
33381         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33382             this.ui.onDisableChange(this, true);
33383         }
33384         this.fireEvent("disabledchange", this, true);
33385     },
33386
33387     /**
33388      * Enables this node
33389      */
33390     enable : function(){
33391         this.disabled = false;
33392         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33393             this.ui.onDisableChange(this, false);
33394         }
33395         this.fireEvent("disabledchange", this, false);
33396     },
33397
33398     // private
33399     renderChildren : function(suppressEvent){
33400         if(suppressEvent !== false){
33401             this.fireEvent("beforechildrenrendered", this);
33402         }
33403         var cs = this.childNodes;
33404         for(var i = 0, len = cs.length; i < len; i++){
33405             cs[i].render(true);
33406         }
33407         this.childrenRendered = true;
33408     },
33409
33410     // private
33411     sort : function(fn, scope){
33412         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33413         if(this.childrenRendered){
33414             var cs = this.childNodes;
33415             for(var i = 0, len = cs.length; i < len; i++){
33416                 cs[i].render(true);
33417             }
33418         }
33419     },
33420
33421     // private
33422     render : function(bulkRender){
33423         this.ui.render(bulkRender);
33424         if(!this.rendered){
33425             this.rendered = true;
33426             if(this.expanded){
33427                 this.expanded = false;
33428                 this.expand(false, false);
33429             }
33430         }
33431     },
33432
33433     // private
33434     renderIndent : function(deep, refresh){
33435         if(refresh){
33436             this.ui.childIndent = null;
33437         }
33438         this.ui.renderIndent();
33439         if(deep === true && this.childrenRendered){
33440             var cs = this.childNodes;
33441             for(var i = 0, len = cs.length; i < len; i++){
33442                 cs[i].renderIndent(true, refresh);
33443             }
33444         }
33445     }
33446 });/*
33447  * Based on:
33448  * Ext JS Library 1.1.1
33449  * Copyright(c) 2006-2007, Ext JS, LLC.
33450  *
33451  * Originally Released Under LGPL - original licence link has changed is not relivant.
33452  *
33453  * Fork - LGPL
33454  * <script type="text/javascript">
33455  */
33456  
33457 /**
33458  * @class Roo.tree.AsyncTreeNode
33459  * @extends Roo.tree.TreeNode
33460  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33461  * @constructor
33462  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33463  */
33464  Roo.tree.AsyncTreeNode = function(config){
33465     this.loaded = false;
33466     this.loading = false;
33467     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33468     /**
33469     * @event beforeload
33470     * Fires before this node is loaded, return false to cancel
33471     * @param {Node} this This node
33472     */
33473     this.addEvents({'beforeload':true, 'load': true});
33474     /**
33475     * @event load
33476     * Fires when this node is loaded
33477     * @param {Node} this This node
33478     */
33479     /**
33480      * The loader used by this node (defaults to using the tree's defined loader)
33481      * @type TreeLoader
33482      * @property loader
33483      */
33484 };
33485 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33486     expand : function(deep, anim, callback){
33487         if(this.loading){ // if an async load is already running, waiting til it's done
33488             var timer;
33489             var f = function(){
33490                 if(!this.loading){ // done loading
33491                     clearInterval(timer);
33492                     this.expand(deep, anim, callback);
33493                 }
33494             }.createDelegate(this);
33495             timer = setInterval(f, 200);
33496             return;
33497         }
33498         if(!this.loaded){
33499             if(this.fireEvent("beforeload", this) === false){
33500                 return;
33501             }
33502             this.loading = true;
33503             this.ui.beforeLoad(this);
33504             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33505             if(loader){
33506                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33507                 return;
33508             }
33509         }
33510         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33511     },
33512     
33513     /**
33514      * Returns true if this node is currently loading
33515      * @return {Boolean}
33516      */
33517     isLoading : function(){
33518         return this.loading;  
33519     },
33520     
33521     loadComplete : function(deep, anim, callback){
33522         this.loading = false;
33523         this.loaded = true;
33524         this.ui.afterLoad(this);
33525         this.fireEvent("load", this);
33526         this.expand(deep, anim, callback);
33527     },
33528     
33529     /**
33530      * Returns true if this node has been loaded
33531      * @return {Boolean}
33532      */
33533     isLoaded : function(){
33534         return this.loaded;
33535     },
33536     
33537     hasChildNodes : function(){
33538         if(!this.isLeaf() && !this.loaded){
33539             return true;
33540         }else{
33541             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33542         }
33543     },
33544
33545     /**
33546      * Trigger a reload for this node
33547      * @param {Function} callback
33548      */
33549     reload : function(callback){
33550         this.collapse(false, false);
33551         while(this.firstChild){
33552             this.removeChild(this.firstChild);
33553         }
33554         this.childrenRendered = false;
33555         this.loaded = false;
33556         if(this.isHiddenRoot()){
33557             this.expanded = false;
33558         }
33559         this.expand(false, false, callback);
33560     }
33561 });/*
33562  * Based on:
33563  * Ext JS Library 1.1.1
33564  * Copyright(c) 2006-2007, Ext JS, LLC.
33565  *
33566  * Originally Released Under LGPL - original licence link has changed is not relivant.
33567  *
33568  * Fork - LGPL
33569  * <script type="text/javascript">
33570  */
33571  
33572 /**
33573  * @class Roo.tree.TreeNodeUI
33574  * @constructor
33575  * @param {Object} node The node to render
33576  * The TreeNode UI implementation is separate from the
33577  * tree implementation. Unless you are customizing the tree UI,
33578  * you should never have to use this directly.
33579  */
33580 Roo.tree.TreeNodeUI = function(node){
33581     this.node = node;
33582     this.rendered = false;
33583     this.animating = false;
33584     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33585 };
33586
33587 Roo.tree.TreeNodeUI.prototype = {
33588     removeChild : function(node){
33589         if(this.rendered){
33590             this.ctNode.removeChild(node.ui.getEl());
33591         }
33592     },
33593
33594     beforeLoad : function(){
33595          this.addClass("x-tree-node-loading");
33596     },
33597
33598     afterLoad : function(){
33599          this.removeClass("x-tree-node-loading");
33600     },
33601
33602     onTextChange : function(node, text, oldText){
33603         if(this.rendered){
33604             this.textNode.innerHTML = text;
33605         }
33606     },
33607
33608     onDisableChange : function(node, state){
33609         this.disabled = state;
33610         if(state){
33611             this.addClass("x-tree-node-disabled");
33612         }else{
33613             this.removeClass("x-tree-node-disabled");
33614         }
33615     },
33616
33617     onSelectedChange : function(state){
33618         if(state){
33619             this.focus();
33620             this.addClass("x-tree-selected");
33621         }else{
33622             //this.blur();
33623             this.removeClass("x-tree-selected");
33624         }
33625     },
33626
33627     onMove : function(tree, node, oldParent, newParent, index, refNode){
33628         this.childIndent = null;
33629         if(this.rendered){
33630             var targetNode = newParent.ui.getContainer();
33631             if(!targetNode){//target not rendered
33632                 this.holder = document.createElement("div");
33633                 this.holder.appendChild(this.wrap);
33634                 return;
33635             }
33636             var insertBefore = refNode ? refNode.ui.getEl() : null;
33637             if(insertBefore){
33638                 targetNode.insertBefore(this.wrap, insertBefore);
33639             }else{
33640                 targetNode.appendChild(this.wrap);
33641             }
33642             this.node.renderIndent(true);
33643         }
33644     },
33645
33646     addClass : function(cls){
33647         if(this.elNode){
33648             Roo.fly(this.elNode).addClass(cls);
33649         }
33650     },
33651
33652     removeClass : function(cls){
33653         if(this.elNode){
33654             Roo.fly(this.elNode).removeClass(cls);
33655         }
33656     },
33657
33658     remove : function(){
33659         if(this.rendered){
33660             this.holder = document.createElement("div");
33661             this.holder.appendChild(this.wrap);
33662         }
33663     },
33664
33665     fireEvent : function(){
33666         return this.node.fireEvent.apply(this.node, arguments);
33667     },
33668
33669     initEvents : function(){
33670         this.node.on("move", this.onMove, this);
33671         var E = Roo.EventManager;
33672         var a = this.anchor;
33673
33674         var el = Roo.fly(a, '_treeui');
33675
33676         if(Roo.isOpera){ // opera render bug ignores the CSS
33677             el.setStyle("text-decoration", "none");
33678         }
33679
33680         el.on("click", this.onClick, this);
33681         el.on("dblclick", this.onDblClick, this);
33682
33683         if(this.checkbox){
33684             Roo.EventManager.on(this.checkbox,
33685                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33686         }
33687
33688         el.on("contextmenu", this.onContextMenu, this);
33689
33690         var icon = Roo.fly(this.iconNode);
33691         icon.on("click", this.onClick, this);
33692         icon.on("dblclick", this.onDblClick, this);
33693         icon.on("contextmenu", this.onContextMenu, this);
33694         E.on(this.ecNode, "click", this.ecClick, this, true);
33695
33696         if(this.node.disabled){
33697             this.addClass("x-tree-node-disabled");
33698         }
33699         if(this.node.hidden){
33700             this.addClass("x-tree-node-disabled");
33701         }
33702         var ot = this.node.getOwnerTree();
33703         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33704         if(dd && (!this.node.isRoot || ot.rootVisible)){
33705             Roo.dd.Registry.register(this.elNode, {
33706                 node: this.node,
33707                 handles: this.getDDHandles(),
33708                 isHandle: false
33709             });
33710         }
33711     },
33712
33713     getDDHandles : function(){
33714         return [this.iconNode, this.textNode];
33715     },
33716
33717     hide : function(){
33718         if(this.rendered){
33719             this.wrap.style.display = "none";
33720         }
33721     },
33722
33723     show : function(){
33724         if(this.rendered){
33725             this.wrap.style.display = "";
33726         }
33727     },
33728
33729     onContextMenu : function(e){
33730         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33731             e.preventDefault();
33732             this.focus();
33733             this.fireEvent("contextmenu", this.node, e);
33734         }
33735     },
33736
33737     onClick : function(e){
33738         if(this.dropping){
33739             e.stopEvent();
33740             return;
33741         }
33742         if(this.fireEvent("beforeclick", this.node, e) !== false){
33743             if(!this.disabled && this.node.attributes.href){
33744                 this.fireEvent("click", this.node, e);
33745                 return;
33746             }
33747             e.preventDefault();
33748             if(this.disabled){
33749                 return;
33750             }
33751
33752             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33753                 this.node.toggle();
33754             }
33755
33756             this.fireEvent("click", this.node, e);
33757         }else{
33758             e.stopEvent();
33759         }
33760     },
33761
33762     onDblClick : function(e){
33763         e.preventDefault();
33764         if(this.disabled){
33765             return;
33766         }
33767         if(this.checkbox){
33768             this.toggleCheck();
33769         }
33770         if(!this.animating && this.node.hasChildNodes()){
33771             this.node.toggle();
33772         }
33773         this.fireEvent("dblclick", this.node, e);
33774     },
33775
33776     onCheckChange : function(){
33777         var checked = this.checkbox.checked;
33778         this.node.attributes.checked = checked;
33779         this.fireEvent('checkchange', this.node, checked);
33780     },
33781
33782     ecClick : function(e){
33783         if(!this.animating && this.node.hasChildNodes()){
33784             this.node.toggle();
33785         }
33786     },
33787
33788     startDrop : function(){
33789         this.dropping = true;
33790     },
33791
33792     // delayed drop so the click event doesn't get fired on a drop
33793     endDrop : function(){
33794        setTimeout(function(){
33795            this.dropping = false;
33796        }.createDelegate(this), 50);
33797     },
33798
33799     expand : function(){
33800         this.updateExpandIcon();
33801         this.ctNode.style.display = "";
33802     },
33803
33804     focus : function(){
33805         if(!this.node.preventHScroll){
33806             try{this.anchor.focus();
33807             }catch(e){}
33808         }else if(!Roo.isIE){
33809             try{
33810                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33811                 var l = noscroll.scrollLeft;
33812                 this.anchor.focus();
33813                 noscroll.scrollLeft = l;
33814             }catch(e){}
33815         }
33816     },
33817
33818     toggleCheck : function(value){
33819         var cb = this.checkbox;
33820         if(cb){
33821             cb.checked = (value === undefined ? !cb.checked : value);
33822         }
33823     },
33824
33825     blur : function(){
33826         try{
33827             this.anchor.blur();
33828         }catch(e){}
33829     },
33830
33831     animExpand : function(callback){
33832         var ct = Roo.get(this.ctNode);
33833         ct.stopFx();
33834         if(!this.node.hasChildNodes()){
33835             this.updateExpandIcon();
33836             this.ctNode.style.display = "";
33837             Roo.callback(callback);
33838             return;
33839         }
33840         this.animating = true;
33841         this.updateExpandIcon();
33842
33843         ct.slideIn('t', {
33844            callback : function(){
33845                this.animating = false;
33846                Roo.callback(callback);
33847             },
33848             scope: this,
33849             duration: this.node.ownerTree.duration || .25
33850         });
33851     },
33852
33853     highlight : function(){
33854         var tree = this.node.getOwnerTree();
33855         Roo.fly(this.wrap).highlight(
33856             tree.hlColor || "C3DAF9",
33857             {endColor: tree.hlBaseColor}
33858         );
33859     },
33860
33861     collapse : function(){
33862         this.updateExpandIcon();
33863         this.ctNode.style.display = "none";
33864     },
33865
33866     animCollapse : function(callback){
33867         var ct = Roo.get(this.ctNode);
33868         ct.enableDisplayMode('block');
33869         ct.stopFx();
33870
33871         this.animating = true;
33872         this.updateExpandIcon();
33873
33874         ct.slideOut('t', {
33875             callback : function(){
33876                this.animating = false;
33877                Roo.callback(callback);
33878             },
33879             scope: this,
33880             duration: this.node.ownerTree.duration || .25
33881         });
33882     },
33883
33884     getContainer : function(){
33885         return this.ctNode;
33886     },
33887
33888     getEl : function(){
33889         return this.wrap;
33890     },
33891
33892     appendDDGhost : function(ghostNode){
33893         ghostNode.appendChild(this.elNode.cloneNode(true));
33894     },
33895
33896     getDDRepairXY : function(){
33897         return Roo.lib.Dom.getXY(this.iconNode);
33898     },
33899
33900     onRender : function(){
33901         this.render();
33902     },
33903
33904     render : function(bulkRender){
33905         var n = this.node, a = n.attributes;
33906         var targetNode = n.parentNode ?
33907               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33908
33909         if(!this.rendered){
33910             this.rendered = true;
33911
33912             this.renderElements(n, a, targetNode, bulkRender);
33913
33914             if(a.qtip){
33915                if(this.textNode.setAttributeNS){
33916                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33917                    if(a.qtipTitle){
33918                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33919                    }
33920                }else{
33921                    this.textNode.setAttribute("ext:qtip", a.qtip);
33922                    if(a.qtipTitle){
33923                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33924                    }
33925                }
33926             }else if(a.qtipCfg){
33927                 a.qtipCfg.target = Roo.id(this.textNode);
33928                 Roo.QuickTips.register(a.qtipCfg);
33929             }
33930             this.initEvents();
33931             if(!this.node.expanded){
33932                 this.updateExpandIcon();
33933             }
33934         }else{
33935             if(bulkRender === true) {
33936                 targetNode.appendChild(this.wrap);
33937             }
33938         }
33939     },
33940
33941     renderElements : function(n, a, targetNode, bulkRender)
33942     {
33943         // add some indent caching, this helps performance when rendering a large tree
33944         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33945         var t = n.getOwnerTree();
33946         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33947         if (typeof(n.attributes.html) != 'undefined') {
33948             txt = n.attributes.html;
33949         }
33950         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33951         var cb = typeof a.checked == 'boolean';
33952         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33953         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33954             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33955             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33956             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33957             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33958             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33959              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33960                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33961             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33962             "</li>"];
33963
33964         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33965             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33966                                 n.nextSibling.ui.getEl(), buf.join(""));
33967         }else{
33968             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33969         }
33970
33971         this.elNode = this.wrap.childNodes[0];
33972         this.ctNode = this.wrap.childNodes[1];
33973         var cs = this.elNode.childNodes;
33974         this.indentNode = cs[0];
33975         this.ecNode = cs[1];
33976         this.iconNode = cs[2];
33977         var index = 3;
33978         if(cb){
33979             this.checkbox = cs[3];
33980             index++;
33981         }
33982         this.anchor = cs[index];
33983         this.textNode = cs[index].firstChild;
33984     },
33985
33986     getAnchor : function(){
33987         return this.anchor;
33988     },
33989
33990     getTextEl : function(){
33991         return this.textNode;
33992     },
33993
33994     getIconEl : function(){
33995         return this.iconNode;
33996     },
33997
33998     isChecked : function(){
33999         return this.checkbox ? this.checkbox.checked : false;
34000     },
34001
34002     updateExpandIcon : function(){
34003         if(this.rendered){
34004             var n = this.node, c1, c2;
34005             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34006             var hasChild = n.hasChildNodes();
34007             if(hasChild){
34008                 if(n.expanded){
34009                     cls += "-minus";
34010                     c1 = "x-tree-node-collapsed";
34011                     c2 = "x-tree-node-expanded";
34012                 }else{
34013                     cls += "-plus";
34014                     c1 = "x-tree-node-expanded";
34015                     c2 = "x-tree-node-collapsed";
34016                 }
34017                 if(this.wasLeaf){
34018                     this.removeClass("x-tree-node-leaf");
34019                     this.wasLeaf = false;
34020                 }
34021                 if(this.c1 != c1 || this.c2 != c2){
34022                     Roo.fly(this.elNode).replaceClass(c1, c2);
34023                     this.c1 = c1; this.c2 = c2;
34024                 }
34025             }else{
34026                 // this changes non-leafs into leafs if they have no children.
34027                 // it's not very rational behaviour..
34028                 
34029                 if(!this.wasLeaf && this.node.leaf){
34030                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34031                     delete this.c1;
34032                     delete this.c2;
34033                     this.wasLeaf = true;
34034                 }
34035             }
34036             var ecc = "x-tree-ec-icon "+cls;
34037             if(this.ecc != ecc){
34038                 this.ecNode.className = ecc;
34039                 this.ecc = ecc;
34040             }
34041         }
34042     },
34043
34044     getChildIndent : function(){
34045         if(!this.childIndent){
34046             var buf = [];
34047             var p = this.node;
34048             while(p){
34049                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34050                     if(!p.isLast()) {
34051                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34052                     } else {
34053                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34054                     }
34055                 }
34056                 p = p.parentNode;
34057             }
34058             this.childIndent = buf.join("");
34059         }
34060         return this.childIndent;
34061     },
34062
34063     renderIndent : function(){
34064         if(this.rendered){
34065             var indent = "";
34066             var p = this.node.parentNode;
34067             if(p){
34068                 indent = p.ui.getChildIndent();
34069             }
34070             if(this.indentMarkup != indent){ // don't rerender if not required
34071                 this.indentNode.innerHTML = indent;
34072                 this.indentMarkup = indent;
34073             }
34074             this.updateExpandIcon();
34075         }
34076     }
34077 };
34078
34079 Roo.tree.RootTreeNodeUI = function(){
34080     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34081 };
34082 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34083     render : function(){
34084         if(!this.rendered){
34085             var targetNode = this.node.ownerTree.innerCt.dom;
34086             this.node.expanded = true;
34087             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34088             this.wrap = this.ctNode = targetNode.firstChild;
34089         }
34090     },
34091     collapse : function(){
34092     },
34093     expand : function(){
34094     }
34095 });/*
34096  * Based on:
34097  * Ext JS Library 1.1.1
34098  * Copyright(c) 2006-2007, Ext JS, LLC.
34099  *
34100  * Originally Released Under LGPL - original licence link has changed is not relivant.
34101  *
34102  * Fork - LGPL
34103  * <script type="text/javascript">
34104  */
34105 /**
34106  * @class Roo.tree.TreeLoader
34107  * @extends Roo.util.Observable
34108  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34109  * nodes from a specified URL. The response must be a javascript Array definition
34110  * who's elements are node definition objects. eg:
34111  * <pre><code>
34112 {  success : true,
34113    data :      [
34114    
34115     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34116     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34117     ]
34118 }
34119
34120
34121 </code></pre>
34122  * <br><br>
34123  * The old style respose with just an array is still supported, but not recommended.
34124  * <br><br>
34125  *
34126  * A server request is sent, and child nodes are loaded only when a node is expanded.
34127  * The loading node's id is passed to the server under the parameter name "node" to
34128  * enable the server to produce the correct child nodes.
34129  * <br><br>
34130  * To pass extra parameters, an event handler may be attached to the "beforeload"
34131  * event, and the parameters specified in the TreeLoader's baseParams property:
34132  * <pre><code>
34133     myTreeLoader.on("beforeload", function(treeLoader, node) {
34134         this.baseParams.category = node.attributes.category;
34135     }, this);
34136 </code></pre><
34137  * This would pass an HTTP parameter called "category" to the server containing
34138  * the value of the Node's "category" attribute.
34139  * @constructor
34140  * Creates a new Treeloader.
34141  * @param {Object} config A config object containing config properties.
34142  */
34143 Roo.tree.TreeLoader = function(config){
34144     this.baseParams = {};
34145     this.requestMethod = "POST";
34146     Roo.apply(this, config);
34147
34148     this.addEvents({
34149     
34150         /**
34151          * @event beforeload
34152          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34153          * @param {Object} This TreeLoader object.
34154          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34155          * @param {Object} callback The callback function specified in the {@link #load} call.
34156          */
34157         beforeload : true,
34158         /**
34159          * @event load
34160          * Fires when the node has been successfuly loaded.
34161          * @param {Object} This TreeLoader object.
34162          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34163          * @param {Object} response The response object containing the data from the server.
34164          */
34165         load : true,
34166         /**
34167          * @event loadexception
34168          * Fires if the network request failed.
34169          * @param {Object} This TreeLoader object.
34170          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34171          * @param {Object} response The response object containing the data from the server.
34172          */
34173         loadexception : true,
34174         /**
34175          * @event create
34176          * Fires before a node is created, enabling you to return custom Node types 
34177          * @param {Object} This TreeLoader object.
34178          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34179          */
34180         create : true
34181     });
34182
34183     Roo.tree.TreeLoader.superclass.constructor.call(this);
34184 };
34185
34186 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34187     /**
34188     * @cfg {String} dataUrl The URL from which to request a Json string which
34189     * specifies an array of node definition object representing the child nodes
34190     * to be loaded.
34191     */
34192     /**
34193     * @cfg {String} requestMethod either GET or POST
34194     * defaults to POST (due to BC)
34195     * to be loaded.
34196     */
34197     /**
34198     * @cfg {Object} baseParams (optional) An object containing properties which
34199     * specify HTTP parameters to be passed to each request for child nodes.
34200     */
34201     /**
34202     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34203     * created by this loader. If the attributes sent by the server have an attribute in this object,
34204     * they take priority.
34205     */
34206     /**
34207     * @cfg {Object} uiProviders (optional) An object containing properties which
34208     * 
34209     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34210     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34211     * <i>uiProvider</i> attribute of a returned child node is a string rather
34212     * than a reference to a TreeNodeUI implementation, this that string value
34213     * is used as a property name in the uiProviders object. You can define the provider named
34214     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34215     */
34216     uiProviders : {},
34217
34218     /**
34219     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34220     * child nodes before loading.
34221     */
34222     clearOnLoad : true,
34223
34224     /**
34225     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34226     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34227     * Grid query { data : [ .....] }
34228     */
34229     
34230     root : false,
34231      /**
34232     * @cfg {String} queryParam (optional) 
34233     * Name of the query as it will be passed on the querystring (defaults to 'node')
34234     * eg. the request will be ?node=[id]
34235     */
34236     
34237     
34238     queryParam: false,
34239     
34240     /**
34241      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34242      * This is called automatically when a node is expanded, but may be used to reload
34243      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34244      * @param {Roo.tree.TreeNode} node
34245      * @param {Function} callback
34246      */
34247     load : function(node, callback){
34248         if(this.clearOnLoad){
34249             while(node.firstChild){
34250                 node.removeChild(node.firstChild);
34251             }
34252         }
34253         if(node.attributes.children){ // preloaded json children
34254             var cs = node.attributes.children;
34255             for(var i = 0, len = cs.length; i < len; i++){
34256                 node.appendChild(this.createNode(cs[i]));
34257             }
34258             if(typeof callback == "function"){
34259                 callback();
34260             }
34261         }else if(this.dataUrl){
34262             this.requestData(node, callback);
34263         }
34264     },
34265
34266     getParams: function(node){
34267         var buf = [], bp = this.baseParams;
34268         for(var key in bp){
34269             if(typeof bp[key] != "function"){
34270                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34271             }
34272         }
34273         var n = this.queryParam === false ? 'node' : this.queryParam;
34274         buf.push(n + "=", encodeURIComponent(node.id));
34275         return buf.join("");
34276     },
34277
34278     requestData : function(node, callback){
34279         if(this.fireEvent("beforeload", this, node, callback) !== false){
34280             this.transId = Roo.Ajax.request({
34281                 method:this.requestMethod,
34282                 url: this.dataUrl||this.url,
34283                 success: this.handleResponse,
34284                 failure: this.handleFailure,
34285                 scope: this,
34286                 argument: {callback: callback, node: node},
34287                 params: this.getParams(node)
34288             });
34289         }else{
34290             // if the load is cancelled, make sure we notify
34291             // the node that we are done
34292             if(typeof callback == "function"){
34293                 callback();
34294             }
34295         }
34296     },
34297
34298     isLoading : function(){
34299         return this.transId ? true : false;
34300     },
34301
34302     abort : function(){
34303         if(this.isLoading()){
34304             Roo.Ajax.abort(this.transId);
34305         }
34306     },
34307
34308     // private
34309     createNode : function(attr)
34310     {
34311         // apply baseAttrs, nice idea Corey!
34312         if(this.baseAttrs){
34313             Roo.applyIf(attr, this.baseAttrs);
34314         }
34315         if(this.applyLoader !== false){
34316             attr.loader = this;
34317         }
34318         // uiProvider = depreciated..
34319         
34320         if(typeof(attr.uiProvider) == 'string'){
34321            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34322                 /**  eval:var:attr */ eval(attr.uiProvider);
34323         }
34324         if(typeof(this.uiProviders['default']) != 'undefined') {
34325             attr.uiProvider = this.uiProviders['default'];
34326         }
34327         
34328         this.fireEvent('create', this, attr);
34329         
34330         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34331         return(attr.leaf ?
34332                         new Roo.tree.TreeNode(attr) :
34333                         new Roo.tree.AsyncTreeNode(attr));
34334     },
34335
34336     processResponse : function(response, node, callback)
34337     {
34338         var json = response.responseText;
34339         try {
34340             
34341             var o = Roo.decode(json);
34342             
34343             if (this.root === false && typeof(o.success) != undefined) {
34344                 this.root = 'data'; // the default behaviour for list like data..
34345                 }
34346                 
34347             if (this.root !== false &&  !o.success) {
34348                 // it's a failure condition.
34349                 var a = response.argument;
34350                 this.fireEvent("loadexception", this, a.node, response);
34351                 Roo.log("Load failed - should have a handler really");
34352                 return;
34353             }
34354             
34355             
34356             
34357             if (this.root !== false) {
34358                  o = o[this.root];
34359             }
34360             
34361             for(var i = 0, len = o.length; i < len; i++){
34362                 var n = this.createNode(o[i]);
34363                 if(n){
34364                     node.appendChild(n);
34365                 }
34366             }
34367             if(typeof callback == "function"){
34368                 callback(this, node);
34369             }
34370         }catch(e){
34371             this.handleFailure(response);
34372         }
34373     },
34374
34375     handleResponse : function(response){
34376         this.transId = false;
34377         var a = response.argument;
34378         this.processResponse(response, a.node, a.callback);
34379         this.fireEvent("load", this, a.node, response);
34380     },
34381
34382     handleFailure : function(response)
34383     {
34384         // should handle failure better..
34385         this.transId = false;
34386         var a = response.argument;
34387         this.fireEvent("loadexception", this, a.node, response);
34388         if(typeof a.callback == "function"){
34389             a.callback(this, a.node);
34390         }
34391     }
34392 });/*
34393  * Based on:
34394  * Ext JS Library 1.1.1
34395  * Copyright(c) 2006-2007, Ext JS, LLC.
34396  *
34397  * Originally Released Under LGPL - original licence link has changed is not relivant.
34398  *
34399  * Fork - LGPL
34400  * <script type="text/javascript">
34401  */
34402
34403 /**
34404 * @class Roo.tree.TreeFilter
34405 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34406 * @param {TreePanel} tree
34407 * @param {Object} config (optional)
34408  */
34409 Roo.tree.TreeFilter = function(tree, config){
34410     this.tree = tree;
34411     this.filtered = {};
34412     Roo.apply(this, config);
34413 };
34414
34415 Roo.tree.TreeFilter.prototype = {
34416     clearBlank:false,
34417     reverse:false,
34418     autoClear:false,
34419     remove:false,
34420
34421      /**
34422      * Filter the data by a specific attribute.
34423      * @param {String/RegExp} value Either string that the attribute value
34424      * should start with or a RegExp to test against the attribute
34425      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34426      * @param {TreeNode} startNode (optional) The node to start the filter at.
34427      */
34428     filter : function(value, attr, startNode){
34429         attr = attr || "text";
34430         var f;
34431         if(typeof value == "string"){
34432             var vlen = value.length;
34433             // auto clear empty filter
34434             if(vlen == 0 && this.clearBlank){
34435                 this.clear();
34436                 return;
34437             }
34438             value = value.toLowerCase();
34439             f = function(n){
34440                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34441             };
34442         }else if(value.exec){ // regex?
34443             f = function(n){
34444                 return value.test(n.attributes[attr]);
34445             };
34446         }else{
34447             throw 'Illegal filter type, must be string or regex';
34448         }
34449         this.filterBy(f, null, startNode);
34450         },
34451
34452     /**
34453      * Filter by a function. The passed function will be called with each
34454      * node in the tree (or from the startNode). If the function returns true, the node is kept
34455      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34456      * @param {Function} fn The filter function
34457      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34458      */
34459     filterBy : function(fn, scope, startNode){
34460         startNode = startNode || this.tree.root;
34461         if(this.autoClear){
34462             this.clear();
34463         }
34464         var af = this.filtered, rv = this.reverse;
34465         var f = function(n){
34466             if(n == startNode){
34467                 return true;
34468             }
34469             if(af[n.id]){
34470                 return false;
34471             }
34472             var m = fn.call(scope || n, n);
34473             if(!m || rv){
34474                 af[n.id] = n;
34475                 n.ui.hide();
34476                 return false;
34477             }
34478             return true;
34479         };
34480         startNode.cascade(f);
34481         if(this.remove){
34482            for(var id in af){
34483                if(typeof id != "function"){
34484                    var n = af[id];
34485                    if(n && n.parentNode){
34486                        n.parentNode.removeChild(n);
34487                    }
34488                }
34489            }
34490         }
34491     },
34492
34493     /**
34494      * Clears the current filter. Note: with the "remove" option
34495      * set a filter cannot be cleared.
34496      */
34497     clear : function(){
34498         var t = this.tree;
34499         var af = this.filtered;
34500         for(var id in af){
34501             if(typeof id != "function"){
34502                 var n = af[id];
34503                 if(n){
34504                     n.ui.show();
34505                 }
34506             }
34507         }
34508         this.filtered = {};
34509     }
34510 };
34511 /*
34512  * Based on:
34513  * Ext JS Library 1.1.1
34514  * Copyright(c) 2006-2007, Ext JS, LLC.
34515  *
34516  * Originally Released Under LGPL - original licence link has changed is not relivant.
34517  *
34518  * Fork - LGPL
34519  * <script type="text/javascript">
34520  */
34521  
34522
34523 /**
34524  * @class Roo.tree.TreeSorter
34525  * Provides sorting of nodes in a TreePanel
34526  * 
34527  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34528  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34529  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34530  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34531  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34532  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34533  * @constructor
34534  * @param {TreePanel} tree
34535  * @param {Object} config
34536  */
34537 Roo.tree.TreeSorter = function(tree, config){
34538     Roo.apply(this, config);
34539     tree.on("beforechildrenrendered", this.doSort, this);
34540     tree.on("append", this.updateSort, this);
34541     tree.on("insert", this.updateSort, this);
34542     
34543     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34544     var p = this.property || "text";
34545     var sortType = this.sortType;
34546     var fs = this.folderSort;
34547     var cs = this.caseSensitive === true;
34548     var leafAttr = this.leafAttr || 'leaf';
34549
34550     this.sortFn = function(n1, n2){
34551         if(fs){
34552             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34553                 return 1;
34554             }
34555             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34556                 return -1;
34557             }
34558         }
34559         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34560         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34561         if(v1 < v2){
34562                         return dsc ? +1 : -1;
34563                 }else if(v1 > v2){
34564                         return dsc ? -1 : +1;
34565         }else{
34566                 return 0;
34567         }
34568     };
34569 };
34570
34571 Roo.tree.TreeSorter.prototype = {
34572     doSort : function(node){
34573         node.sort(this.sortFn);
34574     },
34575     
34576     compareNodes : function(n1, n2){
34577         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34578     },
34579     
34580     updateSort : function(tree, node){
34581         if(node.childrenRendered){
34582             this.doSort.defer(1, this, [node]);
34583         }
34584     }
34585 };/*
34586  * Based on:
34587  * Ext JS Library 1.1.1
34588  * Copyright(c) 2006-2007, Ext JS, LLC.
34589  *
34590  * Originally Released Under LGPL - original licence link has changed is not relivant.
34591  *
34592  * Fork - LGPL
34593  * <script type="text/javascript">
34594  */
34595
34596 if(Roo.dd.DropZone){
34597     
34598 Roo.tree.TreeDropZone = function(tree, config){
34599     this.allowParentInsert = false;
34600     this.allowContainerDrop = false;
34601     this.appendOnly = false;
34602     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34603     this.tree = tree;
34604     this.lastInsertClass = "x-tree-no-status";
34605     this.dragOverData = {};
34606 };
34607
34608 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34609     ddGroup : "TreeDD",
34610     scroll:  true,
34611     
34612     expandDelay : 1000,
34613     
34614     expandNode : function(node){
34615         if(node.hasChildNodes() && !node.isExpanded()){
34616             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34617         }
34618     },
34619     
34620     queueExpand : function(node){
34621         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34622     },
34623     
34624     cancelExpand : function(){
34625         if(this.expandProcId){
34626             clearTimeout(this.expandProcId);
34627             this.expandProcId = false;
34628         }
34629     },
34630     
34631     isValidDropPoint : function(n, pt, dd, e, data){
34632         if(!n || !data){ return false; }
34633         var targetNode = n.node;
34634         var dropNode = data.node;
34635         // default drop rules
34636         if(!(targetNode && targetNode.isTarget && pt)){
34637             return false;
34638         }
34639         if(pt == "append" && targetNode.allowChildren === false){
34640             return false;
34641         }
34642         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34643             return false;
34644         }
34645         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34646             return false;
34647         }
34648         // reuse the object
34649         var overEvent = this.dragOverData;
34650         overEvent.tree = this.tree;
34651         overEvent.target = targetNode;
34652         overEvent.data = data;
34653         overEvent.point = pt;
34654         overEvent.source = dd;
34655         overEvent.rawEvent = e;
34656         overEvent.dropNode = dropNode;
34657         overEvent.cancel = false;  
34658         var result = this.tree.fireEvent("nodedragover", overEvent);
34659         return overEvent.cancel === false && result !== false;
34660     },
34661     
34662     getDropPoint : function(e, n, dd)
34663     {
34664         var tn = n.node;
34665         if(tn.isRoot){
34666             return tn.allowChildren !== false ? "append" : false; // always append for root
34667         }
34668         var dragEl = n.ddel;
34669         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34670         var y = Roo.lib.Event.getPageY(e);
34671         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34672         
34673         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34674         var noAppend = tn.allowChildren === false;
34675         if(this.appendOnly || tn.parentNode.allowChildren === false){
34676             return noAppend ? false : "append";
34677         }
34678         var noBelow = false;
34679         if(!this.allowParentInsert){
34680             noBelow = tn.hasChildNodes() && tn.isExpanded();
34681         }
34682         var q = (b - t) / (noAppend ? 2 : 3);
34683         if(y >= t && y < (t + q)){
34684             return "above";
34685         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34686             return "below";
34687         }else{
34688             return "append";
34689         }
34690     },
34691     
34692     onNodeEnter : function(n, dd, e, data)
34693     {
34694         this.cancelExpand();
34695     },
34696     
34697     onNodeOver : function(n, dd, e, data)
34698     {
34699        
34700         var pt = this.getDropPoint(e, n, dd);
34701         var node = n.node;
34702         
34703         // auto node expand check
34704         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34705             this.queueExpand(node);
34706         }else if(pt != "append"){
34707             this.cancelExpand();
34708         }
34709         
34710         // set the insert point style on the target node
34711         var returnCls = this.dropNotAllowed;
34712         if(this.isValidDropPoint(n, pt, dd, e, data)){
34713            if(pt){
34714                var el = n.ddel;
34715                var cls;
34716                if(pt == "above"){
34717                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34718                    cls = "x-tree-drag-insert-above";
34719                }else if(pt == "below"){
34720                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34721                    cls = "x-tree-drag-insert-below";
34722                }else{
34723                    returnCls = "x-tree-drop-ok-append";
34724                    cls = "x-tree-drag-append";
34725                }
34726                if(this.lastInsertClass != cls){
34727                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34728                    this.lastInsertClass = cls;
34729                }
34730            }
34731        }
34732        return returnCls;
34733     },
34734     
34735     onNodeOut : function(n, dd, e, data){
34736         
34737         this.cancelExpand();
34738         this.removeDropIndicators(n);
34739     },
34740     
34741     onNodeDrop : function(n, dd, e, data){
34742         var point = this.getDropPoint(e, n, dd);
34743         var targetNode = n.node;
34744         targetNode.ui.startDrop();
34745         if(!this.isValidDropPoint(n, point, dd, e, data)){
34746             targetNode.ui.endDrop();
34747             return false;
34748         }
34749         // first try to find the drop node
34750         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34751         var dropEvent = {
34752             tree : this.tree,
34753             target: targetNode,
34754             data: data,
34755             point: point,
34756             source: dd,
34757             rawEvent: e,
34758             dropNode: dropNode,
34759             cancel: !dropNode   
34760         };
34761         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34762         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34763             targetNode.ui.endDrop();
34764             return false;
34765         }
34766         // allow target changing
34767         targetNode = dropEvent.target;
34768         if(point == "append" && !targetNode.isExpanded()){
34769             targetNode.expand(false, null, function(){
34770                 this.completeDrop(dropEvent);
34771             }.createDelegate(this));
34772         }else{
34773             this.completeDrop(dropEvent);
34774         }
34775         return true;
34776     },
34777     
34778     completeDrop : function(de){
34779         var ns = de.dropNode, p = de.point, t = de.target;
34780         if(!(ns instanceof Array)){
34781             ns = [ns];
34782         }
34783         var n;
34784         for(var i = 0, len = ns.length; i < len; i++){
34785             n = ns[i];
34786             if(p == "above"){
34787                 t.parentNode.insertBefore(n, t);
34788             }else if(p == "below"){
34789                 t.parentNode.insertBefore(n, t.nextSibling);
34790             }else{
34791                 t.appendChild(n);
34792             }
34793         }
34794         n.ui.focus();
34795         if(this.tree.hlDrop){
34796             n.ui.highlight();
34797         }
34798         t.ui.endDrop();
34799         this.tree.fireEvent("nodedrop", de);
34800     },
34801     
34802     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34803         if(this.tree.hlDrop){
34804             dropNode.ui.focus();
34805             dropNode.ui.highlight();
34806         }
34807         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34808     },
34809     
34810     getTree : function(){
34811         return this.tree;
34812     },
34813     
34814     removeDropIndicators : function(n){
34815         if(n && n.ddel){
34816             var el = n.ddel;
34817             Roo.fly(el).removeClass([
34818                     "x-tree-drag-insert-above",
34819                     "x-tree-drag-insert-below",
34820                     "x-tree-drag-append"]);
34821             this.lastInsertClass = "_noclass";
34822         }
34823     },
34824     
34825     beforeDragDrop : function(target, e, id){
34826         this.cancelExpand();
34827         return true;
34828     },
34829     
34830     afterRepair : function(data){
34831         if(data && Roo.enableFx){
34832             data.node.ui.highlight();
34833         }
34834         this.hideProxy();
34835     } 
34836     
34837 });
34838
34839 }
34840 /*
34841  * Based on:
34842  * Ext JS Library 1.1.1
34843  * Copyright(c) 2006-2007, Ext JS, LLC.
34844  *
34845  * Originally Released Under LGPL - original licence link has changed is not relivant.
34846  *
34847  * Fork - LGPL
34848  * <script type="text/javascript">
34849  */
34850  
34851
34852 if(Roo.dd.DragZone){
34853 Roo.tree.TreeDragZone = function(tree, config){
34854     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34855     this.tree = tree;
34856 };
34857
34858 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34859     ddGroup : "TreeDD",
34860    
34861     onBeforeDrag : function(data, e){
34862         var n = data.node;
34863         return n && n.draggable && !n.disabled;
34864     },
34865      
34866     
34867     onInitDrag : function(e){
34868         var data = this.dragData;
34869         this.tree.getSelectionModel().select(data.node);
34870         this.proxy.update("");
34871         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34872         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34873     },
34874     
34875     getRepairXY : function(e, data){
34876         return data.node.ui.getDDRepairXY();
34877     },
34878     
34879     onEndDrag : function(data, e){
34880         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34881         
34882         
34883     },
34884     
34885     onValidDrop : function(dd, e, id){
34886         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34887         this.hideProxy();
34888     },
34889     
34890     beforeInvalidDrop : function(e, id){
34891         // this scrolls the original position back into view
34892         var sm = this.tree.getSelectionModel();
34893         sm.clearSelections();
34894         sm.select(this.dragData.node);
34895     }
34896 });
34897 }/*
34898  * Based on:
34899  * Ext JS Library 1.1.1
34900  * Copyright(c) 2006-2007, Ext JS, LLC.
34901  *
34902  * Originally Released Under LGPL - original licence link has changed is not relivant.
34903  *
34904  * Fork - LGPL
34905  * <script type="text/javascript">
34906  */
34907 /**
34908  * @class Roo.tree.TreeEditor
34909  * @extends Roo.Editor
34910  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34911  * as the editor field.
34912  * @constructor
34913  * @param {Object} config (used to be the tree panel.)
34914  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34915  * 
34916  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34917  * @cfg {Roo.form.TextField|Object} field The field configuration
34918  *
34919  * 
34920  */
34921 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34922     var tree = config;
34923     var field;
34924     if (oldconfig) { // old style..
34925         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34926     } else {
34927         // new style..
34928         tree = config.tree;
34929         config.field = config.field  || {};
34930         config.field.xtype = 'TextField';
34931         field = Roo.factory(config.field, Roo.form);
34932     }
34933     config = config || {};
34934     
34935     
34936     this.addEvents({
34937         /**
34938          * @event beforenodeedit
34939          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34940          * false from the handler of this event.
34941          * @param {Editor} this
34942          * @param {Roo.tree.Node} node 
34943          */
34944         "beforenodeedit" : true
34945     });
34946     
34947     //Roo.log(config);
34948     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34949
34950     this.tree = tree;
34951
34952     tree.on('beforeclick', this.beforeNodeClick, this);
34953     tree.getTreeEl().on('mousedown', this.hide, this);
34954     this.on('complete', this.updateNode, this);
34955     this.on('beforestartedit', this.fitToTree, this);
34956     this.on('startedit', this.bindScroll, this, {delay:10});
34957     this.on('specialkey', this.onSpecialKey, this);
34958 };
34959
34960 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34961     /**
34962      * @cfg {String} alignment
34963      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34964      */
34965     alignment: "l-l",
34966     // inherit
34967     autoSize: false,
34968     /**
34969      * @cfg {Boolean} hideEl
34970      * True to hide the bound element while the editor is displayed (defaults to false)
34971      */
34972     hideEl : false,
34973     /**
34974      * @cfg {String} cls
34975      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34976      */
34977     cls: "x-small-editor x-tree-editor",
34978     /**
34979      * @cfg {Boolean} shim
34980      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34981      */
34982     shim:false,
34983     // inherit
34984     shadow:"frame",
34985     /**
34986      * @cfg {Number} maxWidth
34987      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34988      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34989      * scroll and client offsets into account prior to each edit.
34990      */
34991     maxWidth: 250,
34992
34993     editDelay : 350,
34994
34995     // private
34996     fitToTree : function(ed, el){
34997         var td = this.tree.getTreeEl().dom, nd = el.dom;
34998         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34999             td.scrollLeft = nd.offsetLeft;
35000         }
35001         var w = Math.min(
35002                 this.maxWidth,
35003                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35004         this.setSize(w, '');
35005         
35006         return this.fireEvent('beforenodeedit', this, this.editNode);
35007         
35008     },
35009
35010     // private
35011     triggerEdit : function(node){
35012         this.completeEdit();
35013         this.editNode = node;
35014         this.startEdit(node.ui.textNode, node.text);
35015     },
35016
35017     // private
35018     bindScroll : function(){
35019         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35020     },
35021
35022     // private
35023     beforeNodeClick : function(node, e){
35024         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35025         this.lastClick = new Date();
35026         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35027             e.stopEvent();
35028             this.triggerEdit(node);
35029             return false;
35030         }
35031         return true;
35032     },
35033
35034     // private
35035     updateNode : function(ed, value){
35036         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35037         this.editNode.setText(value);
35038     },
35039
35040     // private
35041     onHide : function(){
35042         Roo.tree.TreeEditor.superclass.onHide.call(this);
35043         if(this.editNode){
35044             this.editNode.ui.focus();
35045         }
35046     },
35047
35048     // private
35049     onSpecialKey : function(field, e){
35050         var k = e.getKey();
35051         if(k == e.ESC){
35052             e.stopEvent();
35053             this.cancelEdit();
35054         }else if(k == e.ENTER && !e.hasModifier()){
35055             e.stopEvent();
35056             this.completeEdit();
35057         }
35058     }
35059 });//<Script type="text/javascript">
35060 /*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070  
35071 /**
35072  * Not documented??? - probably should be...
35073  */
35074
35075 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35076     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35077     
35078     renderElements : function(n, a, targetNode, bulkRender){
35079         //consel.log("renderElements?");
35080         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35081
35082         var t = n.getOwnerTree();
35083         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35084         
35085         var cols = t.columns;
35086         var bw = t.borderWidth;
35087         var c = cols[0];
35088         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35089          var cb = typeof a.checked == "boolean";
35090         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35091         var colcls = 'x-t-' + tid + '-c0';
35092         var buf = [
35093             '<li class="x-tree-node">',
35094             
35095                 
35096                 '<div class="x-tree-node-el ', a.cls,'">',
35097                     // extran...
35098                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35099                 
35100                 
35101                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35102                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35103                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35104                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35105                            (a.iconCls ? ' '+a.iconCls : ''),
35106                            '" unselectable="on" />',
35107                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35108                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35109                              
35110                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35111                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35112                             '<span unselectable="on" qtip="' + tx + '">',
35113                              tx,
35114                              '</span></a>' ,
35115                     '</div>',
35116                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35117                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35118                  ];
35119         for(var i = 1, len = cols.length; i < len; i++){
35120             c = cols[i];
35121             colcls = 'x-t-' + tid + '-c' +i;
35122             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35123             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35124                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35125                       "</div>");
35126          }
35127          
35128          buf.push(
35129             '</a>',
35130             '<div class="x-clear"></div></div>',
35131             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35132             "</li>");
35133         
35134         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35135             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35136                                 n.nextSibling.ui.getEl(), buf.join(""));
35137         }else{
35138             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35139         }
35140         var el = this.wrap.firstChild;
35141         this.elRow = el;
35142         this.elNode = el.firstChild;
35143         this.ranchor = el.childNodes[1];
35144         this.ctNode = this.wrap.childNodes[1];
35145         var cs = el.firstChild.childNodes;
35146         this.indentNode = cs[0];
35147         this.ecNode = cs[1];
35148         this.iconNode = cs[2];
35149         var index = 3;
35150         if(cb){
35151             this.checkbox = cs[3];
35152             index++;
35153         }
35154         this.anchor = cs[index];
35155         
35156         this.textNode = cs[index].firstChild;
35157         
35158         //el.on("click", this.onClick, this);
35159         //el.on("dblclick", this.onDblClick, this);
35160         
35161         
35162        // console.log(this);
35163     },
35164     initEvents : function(){
35165         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35166         
35167             
35168         var a = this.ranchor;
35169
35170         var el = Roo.get(a);
35171
35172         if(Roo.isOpera){ // opera render bug ignores the CSS
35173             el.setStyle("text-decoration", "none");
35174         }
35175
35176         el.on("click", this.onClick, this);
35177         el.on("dblclick", this.onDblClick, this);
35178         el.on("contextmenu", this.onContextMenu, this);
35179         
35180     },
35181     
35182     /*onSelectedChange : function(state){
35183         if(state){
35184             this.focus();
35185             this.addClass("x-tree-selected");
35186         }else{
35187             //this.blur();
35188             this.removeClass("x-tree-selected");
35189         }
35190     },*/
35191     addClass : function(cls){
35192         if(this.elRow){
35193             Roo.fly(this.elRow).addClass(cls);
35194         }
35195         
35196     },
35197     
35198     
35199     removeClass : function(cls){
35200         if(this.elRow){
35201             Roo.fly(this.elRow).removeClass(cls);
35202         }
35203     }
35204
35205     
35206     
35207 });//<Script type="text/javascript">
35208
35209 /*
35210  * Based on:
35211  * Ext JS Library 1.1.1
35212  * Copyright(c) 2006-2007, Ext JS, LLC.
35213  *
35214  * Originally Released Under LGPL - original licence link has changed is not relivant.
35215  *
35216  * Fork - LGPL
35217  * <script type="text/javascript">
35218  */
35219  
35220
35221 /**
35222  * @class Roo.tree.ColumnTree
35223  * @extends Roo.data.TreePanel
35224  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35225  * @cfg {int} borderWidth  compined right/left border allowance
35226  * @constructor
35227  * @param {String/HTMLElement/Element} el The container element
35228  * @param {Object} config
35229  */
35230 Roo.tree.ColumnTree =  function(el, config)
35231 {
35232    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35233    this.addEvents({
35234         /**
35235         * @event resize
35236         * Fire this event on a container when it resizes
35237         * @param {int} w Width
35238         * @param {int} h Height
35239         */
35240        "resize" : true
35241     });
35242     this.on('resize', this.onResize, this);
35243 };
35244
35245 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35246     //lines:false,
35247     
35248     
35249     borderWidth: Roo.isBorderBox ? 0 : 2, 
35250     headEls : false,
35251     
35252     render : function(){
35253         // add the header.....
35254        
35255         Roo.tree.ColumnTree.superclass.render.apply(this);
35256         
35257         this.el.addClass('x-column-tree');
35258         
35259         this.headers = this.el.createChild(
35260             {cls:'x-tree-headers'},this.innerCt.dom);
35261    
35262         var cols = this.columns, c;
35263         var totalWidth = 0;
35264         this.headEls = [];
35265         var  len = cols.length;
35266         for(var i = 0; i < len; i++){
35267              c = cols[i];
35268              totalWidth += c.width;
35269             this.headEls.push(this.headers.createChild({
35270                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35271                  cn: {
35272                      cls:'x-tree-hd-text',
35273                      html: c.header
35274                  },
35275                  style:'width:'+(c.width-this.borderWidth)+'px;'
35276              }));
35277         }
35278         this.headers.createChild({cls:'x-clear'});
35279         // prevent floats from wrapping when clipped
35280         this.headers.setWidth(totalWidth);
35281         //this.innerCt.setWidth(totalWidth);
35282         this.innerCt.setStyle({ overflow: 'auto' });
35283         this.onResize(this.width, this.height);
35284              
35285         
35286     },
35287     onResize : function(w,h)
35288     {
35289         this.height = h;
35290         this.width = w;
35291         // resize cols..
35292         this.innerCt.setWidth(this.width);
35293         this.innerCt.setHeight(this.height-20);
35294         
35295         // headers...
35296         var cols = this.columns, c;
35297         var totalWidth = 0;
35298         var expEl = false;
35299         var len = cols.length;
35300         for(var i = 0; i < len; i++){
35301             c = cols[i];
35302             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35303                 // it's the expander..
35304                 expEl  = this.headEls[i];
35305                 continue;
35306             }
35307             totalWidth += c.width;
35308             
35309         }
35310         if (expEl) {
35311             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35312         }
35313         this.headers.setWidth(w-20);
35314
35315         
35316         
35317         
35318     }
35319 });
35320 /*
35321  * Based on:
35322  * Ext JS Library 1.1.1
35323  * Copyright(c) 2006-2007, Ext JS, LLC.
35324  *
35325  * Originally Released Under LGPL - original licence link has changed is not relivant.
35326  *
35327  * Fork - LGPL
35328  * <script type="text/javascript">
35329  */
35330  
35331 /**
35332  * @class Roo.menu.Menu
35333  * @extends Roo.util.Observable
35334  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35335  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35336  * @constructor
35337  * Creates a new Menu
35338  * @param {Object} config Configuration options
35339  */
35340 Roo.menu.Menu = function(config){
35341     Roo.apply(this, config);
35342     this.id = this.id || Roo.id();
35343     this.addEvents({
35344         /**
35345          * @event beforeshow
35346          * Fires before this menu is displayed
35347          * @param {Roo.menu.Menu} this
35348          */
35349         beforeshow : true,
35350         /**
35351          * @event beforehide
35352          * Fires before this menu is hidden
35353          * @param {Roo.menu.Menu} this
35354          */
35355         beforehide : true,
35356         /**
35357          * @event show
35358          * Fires after this menu is displayed
35359          * @param {Roo.menu.Menu} this
35360          */
35361         show : true,
35362         /**
35363          * @event hide
35364          * Fires after this menu is hidden
35365          * @param {Roo.menu.Menu} this
35366          */
35367         hide : true,
35368         /**
35369          * @event click
35370          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35371          * @param {Roo.menu.Menu} this
35372          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35373          * @param {Roo.EventObject} e
35374          */
35375         click : true,
35376         /**
35377          * @event mouseover
35378          * Fires when the mouse is hovering over this menu
35379          * @param {Roo.menu.Menu} this
35380          * @param {Roo.EventObject} e
35381          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35382          */
35383         mouseover : true,
35384         /**
35385          * @event mouseout
35386          * Fires when the mouse exits this menu
35387          * @param {Roo.menu.Menu} this
35388          * @param {Roo.EventObject} e
35389          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35390          */
35391         mouseout : true,
35392         /**
35393          * @event itemclick
35394          * Fires when a menu item contained in this menu is clicked
35395          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35396          * @param {Roo.EventObject} e
35397          */
35398         itemclick: true
35399     });
35400     if (this.registerMenu) {
35401         Roo.menu.MenuMgr.register(this);
35402     }
35403     
35404     var mis = this.items;
35405     this.items = new Roo.util.MixedCollection();
35406     if(mis){
35407         this.add.apply(this, mis);
35408     }
35409 };
35410
35411 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35412     /**
35413      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35414      */
35415     minWidth : 120,
35416     /**
35417      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35418      * for bottom-right shadow (defaults to "sides")
35419      */
35420     shadow : "sides",
35421     /**
35422      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35423      * this menu (defaults to "tl-tr?")
35424      */
35425     subMenuAlign : "tl-tr?",
35426     /**
35427      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35428      * relative to its element of origin (defaults to "tl-bl?")
35429      */
35430     defaultAlign : "tl-bl?",
35431     /**
35432      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35433      */
35434     allowOtherMenus : false,
35435     /**
35436      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35437      */
35438     registerMenu : true,
35439
35440     hidden:true,
35441
35442     // private
35443     render : function(){
35444         if(this.el){
35445             return;
35446         }
35447         var el = this.el = new Roo.Layer({
35448             cls: "x-menu",
35449             shadow:this.shadow,
35450             constrain: false,
35451             parentEl: this.parentEl || document.body,
35452             zindex:15000
35453         });
35454
35455         this.keyNav = new Roo.menu.MenuNav(this);
35456
35457         if(this.plain){
35458             el.addClass("x-menu-plain");
35459         }
35460         if(this.cls){
35461             el.addClass(this.cls);
35462         }
35463         // generic focus element
35464         this.focusEl = el.createChild({
35465             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35466         });
35467         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35468         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35469         
35470         ul.on("mouseover", this.onMouseOver, this);
35471         ul.on("mouseout", this.onMouseOut, this);
35472         this.items.each(function(item){
35473             if (item.hidden) {
35474                 return;
35475             }
35476             
35477             var li = document.createElement("li");
35478             li.className = "x-menu-list-item";
35479             ul.dom.appendChild(li);
35480             item.render(li, this);
35481         }, this);
35482         this.ul = ul;
35483         this.autoWidth();
35484     },
35485
35486     // private
35487     autoWidth : function(){
35488         var el = this.el, ul = this.ul;
35489         if(!el){
35490             return;
35491         }
35492         var w = this.width;
35493         if(w){
35494             el.setWidth(w);
35495         }else if(Roo.isIE){
35496             el.setWidth(this.minWidth);
35497             var t = el.dom.offsetWidth; // force recalc
35498             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35499         }
35500     },
35501
35502     // private
35503     delayAutoWidth : function(){
35504         if(this.rendered){
35505             if(!this.awTask){
35506                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35507             }
35508             this.awTask.delay(20);
35509         }
35510     },
35511
35512     // private
35513     findTargetItem : function(e){
35514         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35515         if(t && t.menuItemId){
35516             return this.items.get(t.menuItemId);
35517         }
35518     },
35519
35520     // private
35521     onClick : function(e){
35522         Roo.log("menu.onClick");
35523         var t = this.findTargetItem(e);
35524         if(!t){
35525             return;
35526         }
35527         Roo.log(e);
35528         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35529             if(t == this.activeItem && t.shouldDeactivate(e)){
35530                 this.activeItem.deactivate();
35531                 delete this.activeItem;
35532                 return;
35533             }
35534             if(t.canActivate){
35535                 this.setActiveItem(t, true);
35536             }
35537             return;
35538             
35539             
35540         }
35541         
35542         t.onClick(e);
35543         this.fireEvent("click", this, t, e);
35544     },
35545
35546     // private
35547     setActiveItem : function(item, autoExpand){
35548         if(item != this.activeItem){
35549             if(this.activeItem){
35550                 this.activeItem.deactivate();
35551             }
35552             this.activeItem = item;
35553             item.activate(autoExpand);
35554         }else if(autoExpand){
35555             item.expandMenu();
35556         }
35557     },
35558
35559     // private
35560     tryActivate : function(start, step){
35561         var items = this.items;
35562         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35563             var item = items.get(i);
35564             if(!item.disabled && item.canActivate){
35565                 this.setActiveItem(item, false);
35566                 return item;
35567             }
35568         }
35569         return false;
35570     },
35571
35572     // private
35573     onMouseOver : function(e){
35574         var t;
35575         if(t = this.findTargetItem(e)){
35576             if(t.canActivate && !t.disabled){
35577                 this.setActiveItem(t, true);
35578             }
35579         }
35580         this.fireEvent("mouseover", this, e, t);
35581     },
35582
35583     // private
35584     onMouseOut : function(e){
35585         var t;
35586         if(t = this.findTargetItem(e)){
35587             if(t == this.activeItem && t.shouldDeactivate(e)){
35588                 this.activeItem.deactivate();
35589                 delete this.activeItem;
35590             }
35591         }
35592         this.fireEvent("mouseout", this, e, t);
35593     },
35594
35595     /**
35596      * Read-only.  Returns true if the menu is currently displayed, else false.
35597      * @type Boolean
35598      */
35599     isVisible : function(){
35600         return this.el && !this.hidden;
35601     },
35602
35603     /**
35604      * Displays this menu relative to another element
35605      * @param {String/HTMLElement/Roo.Element} element The element to align to
35606      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35607      * the element (defaults to this.defaultAlign)
35608      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35609      */
35610     show : function(el, pos, parentMenu){
35611         this.parentMenu = parentMenu;
35612         if(!this.el){
35613             this.render();
35614         }
35615         this.fireEvent("beforeshow", this);
35616         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35617     },
35618
35619     /**
35620      * Displays this menu at a specific xy position
35621      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35622      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35623      */
35624     showAt : function(xy, parentMenu, /* private: */_e){
35625         this.parentMenu = parentMenu;
35626         if(!this.el){
35627             this.render();
35628         }
35629         if(_e !== false){
35630             this.fireEvent("beforeshow", this);
35631             xy = this.el.adjustForConstraints(xy);
35632         }
35633         this.el.setXY(xy);
35634         this.el.show();
35635         this.hidden = false;
35636         this.focus();
35637         this.fireEvent("show", this);
35638     },
35639
35640     focus : function(){
35641         if(!this.hidden){
35642             this.doFocus.defer(50, this);
35643         }
35644     },
35645
35646     doFocus : function(){
35647         if(!this.hidden){
35648             this.focusEl.focus();
35649         }
35650     },
35651
35652     /**
35653      * Hides this menu and optionally all parent menus
35654      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35655      */
35656     hide : function(deep){
35657         if(this.el && this.isVisible()){
35658             this.fireEvent("beforehide", this);
35659             if(this.activeItem){
35660                 this.activeItem.deactivate();
35661                 this.activeItem = null;
35662             }
35663             this.el.hide();
35664             this.hidden = true;
35665             this.fireEvent("hide", this);
35666         }
35667         if(deep === true && this.parentMenu){
35668             this.parentMenu.hide(true);
35669         }
35670     },
35671
35672     /**
35673      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35674      * Any of the following are valid:
35675      * <ul>
35676      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35677      * <li>An HTMLElement object which will be converted to a menu item</li>
35678      * <li>A menu item config object that will be created as a new menu item</li>
35679      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35680      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35681      * </ul>
35682      * Usage:
35683      * <pre><code>
35684 // Create the menu
35685 var menu = new Roo.menu.Menu();
35686
35687 // Create a menu item to add by reference
35688 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35689
35690 // Add a bunch of items at once using different methods.
35691 // Only the last item added will be returned.
35692 var item = menu.add(
35693     menuItem,                // add existing item by ref
35694     'Dynamic Item',          // new TextItem
35695     '-',                     // new separator
35696     { text: 'Config Item' }  // new item by config
35697 );
35698 </code></pre>
35699      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35700      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35701      */
35702     add : function(){
35703         var a = arguments, l = a.length, item;
35704         for(var i = 0; i < l; i++){
35705             var el = a[i];
35706             if ((typeof(el) == "object") && el.xtype && el.xns) {
35707                 el = Roo.factory(el, Roo.menu);
35708             }
35709             
35710             if(el.render){ // some kind of Item
35711                 item = this.addItem(el);
35712             }else if(typeof el == "string"){ // string
35713                 if(el == "separator" || el == "-"){
35714                     item = this.addSeparator();
35715                 }else{
35716                     item = this.addText(el);
35717                 }
35718             }else if(el.tagName || el.el){ // element
35719                 item = this.addElement(el);
35720             }else if(typeof el == "object"){ // must be menu item config?
35721                 item = this.addMenuItem(el);
35722             }
35723         }
35724         return item;
35725     },
35726
35727     /**
35728      * Returns this menu's underlying {@link Roo.Element} object
35729      * @return {Roo.Element} The element
35730      */
35731     getEl : function(){
35732         if(!this.el){
35733             this.render();
35734         }
35735         return this.el;
35736     },
35737
35738     /**
35739      * Adds a separator bar to the menu
35740      * @return {Roo.menu.Item} The menu item that was added
35741      */
35742     addSeparator : function(){
35743         return this.addItem(new Roo.menu.Separator());
35744     },
35745
35746     /**
35747      * Adds an {@link Roo.Element} object to the menu
35748      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35749      * @return {Roo.menu.Item} The menu item that was added
35750      */
35751     addElement : function(el){
35752         return this.addItem(new Roo.menu.BaseItem(el));
35753     },
35754
35755     /**
35756      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35757      * @param {Roo.menu.Item} item The menu item to add
35758      * @return {Roo.menu.Item} The menu item that was added
35759      */
35760     addItem : function(item){
35761         this.items.add(item);
35762         if(this.ul){
35763             var li = document.createElement("li");
35764             li.className = "x-menu-list-item";
35765             this.ul.dom.appendChild(li);
35766             item.render(li, this);
35767             this.delayAutoWidth();
35768         }
35769         return item;
35770     },
35771
35772     /**
35773      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35774      * @param {Object} config A MenuItem config object
35775      * @return {Roo.menu.Item} The menu item that was added
35776      */
35777     addMenuItem : function(config){
35778         if(!(config instanceof Roo.menu.Item)){
35779             if(typeof config.checked == "boolean"){ // must be check menu item config?
35780                 config = new Roo.menu.CheckItem(config);
35781             }else{
35782                 config = new Roo.menu.Item(config);
35783             }
35784         }
35785         return this.addItem(config);
35786     },
35787
35788     /**
35789      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35790      * @param {String} text The text to display in the menu item
35791      * @return {Roo.menu.Item} The menu item that was added
35792      */
35793     addText : function(text){
35794         return this.addItem(new Roo.menu.TextItem({ text : text }));
35795     },
35796
35797     /**
35798      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35799      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35800      * @param {Roo.menu.Item} item The menu item to add
35801      * @return {Roo.menu.Item} The menu item that was added
35802      */
35803     insert : function(index, item){
35804         this.items.insert(index, item);
35805         if(this.ul){
35806             var li = document.createElement("li");
35807             li.className = "x-menu-list-item";
35808             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35809             item.render(li, this);
35810             this.delayAutoWidth();
35811         }
35812         return item;
35813     },
35814
35815     /**
35816      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35817      * @param {Roo.menu.Item} item The menu item to remove
35818      */
35819     remove : function(item){
35820         this.items.removeKey(item.id);
35821         item.destroy();
35822     },
35823
35824     /**
35825      * Removes and destroys all items in the menu
35826      */
35827     removeAll : function(){
35828         var f;
35829         while(f = this.items.first()){
35830             this.remove(f);
35831         }
35832     }
35833 });
35834
35835 // MenuNav is a private utility class used internally by the Menu
35836 Roo.menu.MenuNav = function(menu){
35837     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35838     this.scope = this.menu = menu;
35839 };
35840
35841 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35842     doRelay : function(e, h){
35843         var k = e.getKey();
35844         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35845             this.menu.tryActivate(0, 1);
35846             return false;
35847         }
35848         return h.call(this.scope || this, e, this.menu);
35849     },
35850
35851     up : function(e, m){
35852         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35853             m.tryActivate(m.items.length-1, -1);
35854         }
35855     },
35856
35857     down : function(e, m){
35858         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35859             m.tryActivate(0, 1);
35860         }
35861     },
35862
35863     right : function(e, m){
35864         if(m.activeItem){
35865             m.activeItem.expandMenu(true);
35866         }
35867     },
35868
35869     left : function(e, m){
35870         m.hide();
35871         if(m.parentMenu && m.parentMenu.activeItem){
35872             m.parentMenu.activeItem.activate();
35873         }
35874     },
35875
35876     enter : function(e, m){
35877         if(m.activeItem){
35878             e.stopPropagation();
35879             m.activeItem.onClick(e);
35880             m.fireEvent("click", this, m.activeItem);
35881             return true;
35882         }
35883     }
35884 });/*
35885  * Based on:
35886  * Ext JS Library 1.1.1
35887  * Copyright(c) 2006-2007, Ext JS, LLC.
35888  *
35889  * Originally Released Under LGPL - original licence link has changed is not relivant.
35890  *
35891  * Fork - LGPL
35892  * <script type="text/javascript">
35893  */
35894  
35895 /**
35896  * @class Roo.menu.MenuMgr
35897  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35898  * @singleton
35899  */
35900 Roo.menu.MenuMgr = function(){
35901    var menus, active, groups = {}, attached = false, lastShow = new Date();
35902
35903    // private - called when first menu is created
35904    function init(){
35905        menus = {};
35906        active = new Roo.util.MixedCollection();
35907        Roo.get(document).addKeyListener(27, function(){
35908            if(active.length > 0){
35909                hideAll();
35910            }
35911        });
35912    }
35913
35914    // private
35915    function hideAll(){
35916        if(active && active.length > 0){
35917            var c = active.clone();
35918            c.each(function(m){
35919                m.hide();
35920            });
35921        }
35922    }
35923
35924    // private
35925    function onHide(m){
35926        active.remove(m);
35927        if(active.length < 1){
35928            Roo.get(document).un("mousedown", onMouseDown);
35929            attached = false;
35930        }
35931    }
35932
35933    // private
35934    function onShow(m){
35935        var last = active.last();
35936        lastShow = new Date();
35937        active.add(m);
35938        if(!attached){
35939            Roo.get(document).on("mousedown", onMouseDown);
35940            attached = true;
35941        }
35942        if(m.parentMenu){
35943           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35944           m.parentMenu.activeChild = m;
35945        }else if(last && last.isVisible()){
35946           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35947        }
35948    }
35949
35950    // private
35951    function onBeforeHide(m){
35952        if(m.activeChild){
35953            m.activeChild.hide();
35954        }
35955        if(m.autoHideTimer){
35956            clearTimeout(m.autoHideTimer);
35957            delete m.autoHideTimer;
35958        }
35959    }
35960
35961    // private
35962    function onBeforeShow(m){
35963        var pm = m.parentMenu;
35964        if(!pm && !m.allowOtherMenus){
35965            hideAll();
35966        }else if(pm && pm.activeChild && active != m){
35967            pm.activeChild.hide();
35968        }
35969    }
35970
35971    // private
35972    function onMouseDown(e){
35973        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35974            hideAll();
35975        }
35976    }
35977
35978    // private
35979    function onBeforeCheck(mi, state){
35980        if(state){
35981            var g = groups[mi.group];
35982            for(var i = 0, l = g.length; i < l; i++){
35983                if(g[i] != mi){
35984                    g[i].setChecked(false);
35985                }
35986            }
35987        }
35988    }
35989
35990    return {
35991
35992        /**
35993         * Hides all menus that are currently visible
35994         */
35995        hideAll : function(){
35996             hideAll();  
35997        },
35998
35999        // private
36000        register : function(menu){
36001            if(!menus){
36002                init();
36003            }
36004            menus[menu.id] = menu;
36005            menu.on("beforehide", onBeforeHide);
36006            menu.on("hide", onHide);
36007            menu.on("beforeshow", onBeforeShow);
36008            menu.on("show", onShow);
36009            var g = menu.group;
36010            if(g && menu.events["checkchange"]){
36011                if(!groups[g]){
36012                    groups[g] = [];
36013                }
36014                groups[g].push(menu);
36015                menu.on("checkchange", onCheck);
36016            }
36017        },
36018
36019         /**
36020          * Returns a {@link Roo.menu.Menu} object
36021          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36022          * be used to generate and return a new Menu instance.
36023          */
36024        get : function(menu){
36025            if(typeof menu == "string"){ // menu id
36026                return menus[menu];
36027            }else if(menu.events){  // menu instance
36028                return menu;
36029            }else if(typeof menu.length == 'number'){ // array of menu items?
36030                return new Roo.menu.Menu({items:menu});
36031            }else{ // otherwise, must be a config
36032                return new Roo.menu.Menu(menu);
36033            }
36034        },
36035
36036        // private
36037        unregister : function(menu){
36038            delete menus[menu.id];
36039            menu.un("beforehide", onBeforeHide);
36040            menu.un("hide", onHide);
36041            menu.un("beforeshow", onBeforeShow);
36042            menu.un("show", onShow);
36043            var g = menu.group;
36044            if(g && menu.events["checkchange"]){
36045                groups[g].remove(menu);
36046                menu.un("checkchange", onCheck);
36047            }
36048        },
36049
36050        // private
36051        registerCheckable : function(menuItem){
36052            var g = menuItem.group;
36053            if(g){
36054                if(!groups[g]){
36055                    groups[g] = [];
36056                }
36057                groups[g].push(menuItem);
36058                menuItem.on("beforecheckchange", onBeforeCheck);
36059            }
36060        },
36061
36062        // private
36063        unregisterCheckable : function(menuItem){
36064            var g = menuItem.group;
36065            if(g){
36066                groups[g].remove(menuItem);
36067                menuItem.un("beforecheckchange", onBeforeCheck);
36068            }
36069        }
36070    };
36071 }();/*
36072  * Based on:
36073  * Ext JS Library 1.1.1
36074  * Copyright(c) 2006-2007, Ext JS, LLC.
36075  *
36076  * Originally Released Under LGPL - original licence link has changed is not relivant.
36077  *
36078  * Fork - LGPL
36079  * <script type="text/javascript">
36080  */
36081  
36082
36083 /**
36084  * @class Roo.menu.BaseItem
36085  * @extends Roo.Component
36086  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36087  * management and base configuration options shared by all menu components.
36088  * @constructor
36089  * Creates a new BaseItem
36090  * @param {Object} config Configuration options
36091  */
36092 Roo.menu.BaseItem = function(config){
36093     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36094
36095     this.addEvents({
36096         /**
36097          * @event click
36098          * Fires when this item is clicked
36099          * @param {Roo.menu.BaseItem} this
36100          * @param {Roo.EventObject} e
36101          */
36102         click: true,
36103         /**
36104          * @event activate
36105          * Fires when this item is activated
36106          * @param {Roo.menu.BaseItem} this
36107          */
36108         activate : true,
36109         /**
36110          * @event deactivate
36111          * Fires when this item is deactivated
36112          * @param {Roo.menu.BaseItem} this
36113          */
36114         deactivate : true
36115     });
36116
36117     if(this.handler){
36118         this.on("click", this.handler, this.scope, true);
36119     }
36120 };
36121
36122 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36123     /**
36124      * @cfg {Function} handler
36125      * A function that will handle the click event of this menu item (defaults to undefined)
36126      */
36127     /**
36128      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36129      */
36130     canActivate : false,
36131     
36132      /**
36133      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36134      */
36135     hidden: false,
36136     
36137     /**
36138      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36139      */
36140     activeClass : "x-menu-item-active",
36141     /**
36142      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36143      */
36144     hideOnClick : true,
36145     /**
36146      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36147      */
36148     hideDelay : 100,
36149
36150     // private
36151     ctype: "Roo.menu.BaseItem",
36152
36153     // private
36154     actionMode : "container",
36155
36156     // private
36157     render : function(container, parentMenu){
36158         this.parentMenu = parentMenu;
36159         Roo.menu.BaseItem.superclass.render.call(this, container);
36160         this.container.menuItemId = this.id;
36161     },
36162
36163     // private
36164     onRender : function(container, position){
36165         this.el = Roo.get(this.el);
36166         container.dom.appendChild(this.el.dom);
36167     },
36168
36169     // private
36170     onClick : function(e){
36171         if(!this.disabled && this.fireEvent("click", this, e) !== false
36172                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36173             this.handleClick(e);
36174         }else{
36175             e.stopEvent();
36176         }
36177     },
36178
36179     // private
36180     activate : function(){
36181         if(this.disabled){
36182             return false;
36183         }
36184         var li = this.container;
36185         li.addClass(this.activeClass);
36186         this.region = li.getRegion().adjust(2, 2, -2, -2);
36187         this.fireEvent("activate", this);
36188         return true;
36189     },
36190
36191     // private
36192     deactivate : function(){
36193         this.container.removeClass(this.activeClass);
36194         this.fireEvent("deactivate", this);
36195     },
36196
36197     // private
36198     shouldDeactivate : function(e){
36199         return !this.region || !this.region.contains(e.getPoint());
36200     },
36201
36202     // private
36203     handleClick : function(e){
36204         if(this.hideOnClick){
36205             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36206         }
36207     },
36208
36209     // private
36210     expandMenu : function(autoActivate){
36211         // do nothing
36212     },
36213
36214     // private
36215     hideMenu : function(){
36216         // do nothing
36217     }
36218 });/*
36219  * Based on:
36220  * Ext JS Library 1.1.1
36221  * Copyright(c) 2006-2007, Ext JS, LLC.
36222  *
36223  * Originally Released Under LGPL - original licence link has changed is not relivant.
36224  *
36225  * Fork - LGPL
36226  * <script type="text/javascript">
36227  */
36228  
36229 /**
36230  * @class Roo.menu.Adapter
36231  * @extends Roo.menu.BaseItem
36232  * 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.
36233  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36234  * @constructor
36235  * Creates a new Adapter
36236  * @param {Object} config Configuration options
36237  */
36238 Roo.menu.Adapter = function(component, config){
36239     Roo.menu.Adapter.superclass.constructor.call(this, config);
36240     this.component = component;
36241 };
36242 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36243     // private
36244     canActivate : true,
36245
36246     // private
36247     onRender : function(container, position){
36248         this.component.render(container);
36249         this.el = this.component.getEl();
36250     },
36251
36252     // private
36253     activate : function(){
36254         if(this.disabled){
36255             return false;
36256         }
36257         this.component.focus();
36258         this.fireEvent("activate", this);
36259         return true;
36260     },
36261
36262     // private
36263     deactivate : function(){
36264         this.fireEvent("deactivate", this);
36265     },
36266
36267     // private
36268     disable : function(){
36269         this.component.disable();
36270         Roo.menu.Adapter.superclass.disable.call(this);
36271     },
36272
36273     // private
36274     enable : function(){
36275         this.component.enable();
36276         Roo.menu.Adapter.superclass.enable.call(this);
36277     }
36278 });/*
36279  * Based on:
36280  * Ext JS Library 1.1.1
36281  * Copyright(c) 2006-2007, Ext JS, LLC.
36282  *
36283  * Originally Released Under LGPL - original licence link has changed is not relivant.
36284  *
36285  * Fork - LGPL
36286  * <script type="text/javascript">
36287  */
36288
36289 /**
36290  * @class Roo.menu.TextItem
36291  * @extends Roo.menu.BaseItem
36292  * Adds a static text string to a menu, usually used as either a heading or group separator.
36293  * Note: old style constructor with text is still supported.
36294  * 
36295  * @constructor
36296  * Creates a new TextItem
36297  * @param {Object} cfg Configuration
36298  */
36299 Roo.menu.TextItem = function(cfg){
36300     if (typeof(cfg) == 'string') {
36301         this.text = cfg;
36302     } else {
36303         Roo.apply(this,cfg);
36304     }
36305     
36306     Roo.menu.TextItem.superclass.constructor.call(this);
36307 };
36308
36309 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36310     /**
36311      * @cfg {Boolean} text Text to show on item.
36312      */
36313     text : '',
36314     
36315     /**
36316      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36317      */
36318     hideOnClick : false,
36319     /**
36320      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36321      */
36322     itemCls : "x-menu-text",
36323
36324     // private
36325     onRender : function(){
36326         var s = document.createElement("span");
36327         s.className = this.itemCls;
36328         s.innerHTML = this.text;
36329         this.el = s;
36330         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36331     }
36332 });/*
36333  * Based on:
36334  * Ext JS Library 1.1.1
36335  * Copyright(c) 2006-2007, Ext JS, LLC.
36336  *
36337  * Originally Released Under LGPL - original licence link has changed is not relivant.
36338  *
36339  * Fork - LGPL
36340  * <script type="text/javascript">
36341  */
36342
36343 /**
36344  * @class Roo.menu.Separator
36345  * @extends Roo.menu.BaseItem
36346  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36347  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36348  * @constructor
36349  * @param {Object} config Configuration options
36350  */
36351 Roo.menu.Separator = function(config){
36352     Roo.menu.Separator.superclass.constructor.call(this, config);
36353 };
36354
36355 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36356     /**
36357      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36358      */
36359     itemCls : "x-menu-sep",
36360     /**
36361      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36362      */
36363     hideOnClick : false,
36364
36365     // private
36366     onRender : function(li){
36367         var s = document.createElement("span");
36368         s.className = this.itemCls;
36369         s.innerHTML = "&#160;";
36370         this.el = s;
36371         li.addClass("x-menu-sep-li");
36372         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36373     }
36374 });/*
36375  * Based on:
36376  * Ext JS Library 1.1.1
36377  * Copyright(c) 2006-2007, Ext JS, LLC.
36378  *
36379  * Originally Released Under LGPL - original licence link has changed is not relivant.
36380  *
36381  * Fork - LGPL
36382  * <script type="text/javascript">
36383  */
36384 /**
36385  * @class Roo.menu.Item
36386  * @extends Roo.menu.BaseItem
36387  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36388  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36389  * activation and click handling.
36390  * @constructor
36391  * Creates a new Item
36392  * @param {Object} config Configuration options
36393  */
36394 Roo.menu.Item = function(config){
36395     Roo.menu.Item.superclass.constructor.call(this, config);
36396     if(this.menu){
36397         this.menu = Roo.menu.MenuMgr.get(this.menu);
36398     }
36399 };
36400 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36401     
36402     /**
36403      * @cfg {String} text
36404      * The text to show on the menu item.
36405      */
36406     text: '',
36407      /**
36408      * @cfg {String} HTML to render in menu
36409      * The text to show on the menu item (HTML version).
36410      */
36411     html: '',
36412     /**
36413      * @cfg {String} icon
36414      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36415      */
36416     icon: undefined,
36417     /**
36418      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36419      */
36420     itemCls : "x-menu-item",
36421     /**
36422      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36423      */
36424     canActivate : true,
36425     /**
36426      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36427      */
36428     showDelay: 200,
36429     // doc'd in BaseItem
36430     hideDelay: 200,
36431
36432     // private
36433     ctype: "Roo.menu.Item",
36434     
36435     // private
36436     onRender : function(container, position){
36437         var el = document.createElement("a");
36438         el.hideFocus = true;
36439         el.unselectable = "on";
36440         el.href = this.href || "#";
36441         if(this.hrefTarget){
36442             el.target = this.hrefTarget;
36443         }
36444         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36445         
36446         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36447         
36448         el.innerHTML = String.format(
36449                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36450                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36451         this.el = el;
36452         Roo.menu.Item.superclass.onRender.call(this, container, position);
36453     },
36454
36455     /**
36456      * Sets the text to display in this menu item
36457      * @param {String} text The text to display
36458      * @param {Boolean} isHTML true to indicate text is pure html.
36459      */
36460     setText : function(text, isHTML){
36461         if (isHTML) {
36462             this.html = text;
36463         } else {
36464             this.text = text;
36465             this.html = '';
36466         }
36467         if(this.rendered){
36468             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36469      
36470             this.el.update(String.format(
36471                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36472                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36473             this.parentMenu.autoWidth();
36474         }
36475     },
36476
36477     // private
36478     handleClick : function(e){
36479         if(!this.href){ // if no link defined, stop the event automatically
36480             e.stopEvent();
36481         }
36482         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36483     },
36484
36485     // private
36486     activate : function(autoExpand){
36487         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36488             this.focus();
36489             if(autoExpand){
36490                 this.expandMenu();
36491             }
36492         }
36493         return true;
36494     },
36495
36496     // private
36497     shouldDeactivate : function(e){
36498         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36499             if(this.menu && this.menu.isVisible()){
36500                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36501             }
36502             return true;
36503         }
36504         return false;
36505     },
36506
36507     // private
36508     deactivate : function(){
36509         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36510         this.hideMenu();
36511     },
36512
36513     // private
36514     expandMenu : function(autoActivate){
36515         if(!this.disabled && this.menu){
36516             clearTimeout(this.hideTimer);
36517             delete this.hideTimer;
36518             if(!this.menu.isVisible() && !this.showTimer){
36519                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36520             }else if (this.menu.isVisible() && autoActivate){
36521                 this.menu.tryActivate(0, 1);
36522             }
36523         }
36524     },
36525
36526     // private
36527     deferExpand : function(autoActivate){
36528         delete this.showTimer;
36529         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36530         if(autoActivate){
36531             this.menu.tryActivate(0, 1);
36532         }
36533     },
36534
36535     // private
36536     hideMenu : function(){
36537         clearTimeout(this.showTimer);
36538         delete this.showTimer;
36539         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36540             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36541         }
36542     },
36543
36544     // private
36545     deferHide : function(){
36546         delete this.hideTimer;
36547         this.menu.hide();
36548     }
36549 });/*
36550  * Based on:
36551  * Ext JS Library 1.1.1
36552  * Copyright(c) 2006-2007, Ext JS, LLC.
36553  *
36554  * Originally Released Under LGPL - original licence link has changed is not relivant.
36555  *
36556  * Fork - LGPL
36557  * <script type="text/javascript">
36558  */
36559  
36560 /**
36561  * @class Roo.menu.CheckItem
36562  * @extends Roo.menu.Item
36563  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36564  * @constructor
36565  * Creates a new CheckItem
36566  * @param {Object} config Configuration options
36567  */
36568 Roo.menu.CheckItem = function(config){
36569     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36570     this.addEvents({
36571         /**
36572          * @event beforecheckchange
36573          * Fires before the checked value is set, providing an opportunity to cancel if needed
36574          * @param {Roo.menu.CheckItem} this
36575          * @param {Boolean} checked The new checked value that will be set
36576          */
36577         "beforecheckchange" : true,
36578         /**
36579          * @event checkchange
36580          * Fires after the checked value has been set
36581          * @param {Roo.menu.CheckItem} this
36582          * @param {Boolean} checked The checked value that was set
36583          */
36584         "checkchange" : true
36585     });
36586     if(this.checkHandler){
36587         this.on('checkchange', this.checkHandler, this.scope);
36588     }
36589 };
36590 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36591     /**
36592      * @cfg {String} group
36593      * All check items with the same group name will automatically be grouped into a single-select
36594      * radio button group (defaults to '')
36595      */
36596     /**
36597      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36598      */
36599     itemCls : "x-menu-item x-menu-check-item",
36600     /**
36601      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36602      */
36603     groupClass : "x-menu-group-item",
36604
36605     /**
36606      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36607      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36608      * initialized with checked = true will be rendered as checked.
36609      */
36610     checked: false,
36611
36612     // private
36613     ctype: "Roo.menu.CheckItem",
36614
36615     // private
36616     onRender : function(c){
36617         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36618         if(this.group){
36619             this.el.addClass(this.groupClass);
36620         }
36621         Roo.menu.MenuMgr.registerCheckable(this);
36622         if(this.checked){
36623             this.checked = false;
36624             this.setChecked(true, true);
36625         }
36626     },
36627
36628     // private
36629     destroy : function(){
36630         if(this.rendered){
36631             Roo.menu.MenuMgr.unregisterCheckable(this);
36632         }
36633         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36634     },
36635
36636     /**
36637      * Set the checked state of this item
36638      * @param {Boolean} checked The new checked value
36639      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36640      */
36641     setChecked : function(state, suppressEvent){
36642         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36643             if(this.container){
36644                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36645             }
36646             this.checked = state;
36647             if(suppressEvent !== true){
36648                 this.fireEvent("checkchange", this, state);
36649             }
36650         }
36651     },
36652
36653     // private
36654     handleClick : function(e){
36655        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36656            this.setChecked(!this.checked);
36657        }
36658        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36659     }
36660 });/*
36661  * Based on:
36662  * Ext JS Library 1.1.1
36663  * Copyright(c) 2006-2007, Ext JS, LLC.
36664  *
36665  * Originally Released Under LGPL - original licence link has changed is not relivant.
36666  *
36667  * Fork - LGPL
36668  * <script type="text/javascript">
36669  */
36670  
36671 /**
36672  * @class Roo.menu.DateItem
36673  * @extends Roo.menu.Adapter
36674  * A menu item that wraps the {@link Roo.DatPicker} component.
36675  * @constructor
36676  * Creates a new DateItem
36677  * @param {Object} config Configuration options
36678  */
36679 Roo.menu.DateItem = function(config){
36680     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36681     /** The Roo.DatePicker object @type Roo.DatePicker */
36682     this.picker = this.component;
36683     this.addEvents({select: true});
36684     
36685     this.picker.on("render", function(picker){
36686         picker.getEl().swallowEvent("click");
36687         picker.container.addClass("x-menu-date-item");
36688     });
36689
36690     this.picker.on("select", this.onSelect, this);
36691 };
36692
36693 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36694     // private
36695     onSelect : function(picker, date){
36696         this.fireEvent("select", this, date, picker);
36697         Roo.menu.DateItem.superclass.handleClick.call(this);
36698     }
36699 });/*
36700  * Based on:
36701  * Ext JS Library 1.1.1
36702  * Copyright(c) 2006-2007, Ext JS, LLC.
36703  *
36704  * Originally Released Under LGPL - original licence link has changed is not relivant.
36705  *
36706  * Fork - LGPL
36707  * <script type="text/javascript">
36708  */
36709  
36710 /**
36711  * @class Roo.menu.ColorItem
36712  * @extends Roo.menu.Adapter
36713  * A menu item that wraps the {@link Roo.ColorPalette} component.
36714  * @constructor
36715  * Creates a new ColorItem
36716  * @param {Object} config Configuration options
36717  */
36718 Roo.menu.ColorItem = function(config){
36719     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36720     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36721     this.palette = this.component;
36722     this.relayEvents(this.palette, ["select"]);
36723     if(this.selectHandler){
36724         this.on('select', this.selectHandler, this.scope);
36725     }
36726 };
36727 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36728  * Based on:
36729  * Ext JS Library 1.1.1
36730  * Copyright(c) 2006-2007, Ext JS, LLC.
36731  *
36732  * Originally Released Under LGPL - original licence link has changed is not relivant.
36733  *
36734  * Fork - LGPL
36735  * <script type="text/javascript">
36736  */
36737  
36738
36739 /**
36740  * @class Roo.menu.DateMenu
36741  * @extends Roo.menu.Menu
36742  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36743  * @constructor
36744  * Creates a new DateMenu
36745  * @param {Object} config Configuration options
36746  */
36747 Roo.menu.DateMenu = function(config){
36748     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36749     this.plain = true;
36750     var di = new Roo.menu.DateItem(config);
36751     this.add(di);
36752     /**
36753      * The {@link Roo.DatePicker} instance for this DateMenu
36754      * @type DatePicker
36755      */
36756     this.picker = di.picker;
36757     /**
36758      * @event select
36759      * @param {DatePicker} picker
36760      * @param {Date} date
36761      */
36762     this.relayEvents(di, ["select"]);
36763     this.on('beforeshow', function(){
36764         if(this.picker){
36765             this.picker.hideMonthPicker(false);
36766         }
36767     }, this);
36768 };
36769 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36770     cls:'x-date-menu'
36771 });/*
36772  * Based on:
36773  * Ext JS Library 1.1.1
36774  * Copyright(c) 2006-2007, Ext JS, LLC.
36775  *
36776  * Originally Released Under LGPL - original licence link has changed is not relivant.
36777  *
36778  * Fork - LGPL
36779  * <script type="text/javascript">
36780  */
36781  
36782
36783 /**
36784  * @class Roo.menu.ColorMenu
36785  * @extends Roo.menu.Menu
36786  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36787  * @constructor
36788  * Creates a new ColorMenu
36789  * @param {Object} config Configuration options
36790  */
36791 Roo.menu.ColorMenu = function(config){
36792     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36793     this.plain = true;
36794     var ci = new Roo.menu.ColorItem(config);
36795     this.add(ci);
36796     /**
36797      * The {@link Roo.ColorPalette} instance for this ColorMenu
36798      * @type ColorPalette
36799      */
36800     this.palette = ci.palette;
36801     /**
36802      * @event select
36803      * @param {ColorPalette} palette
36804      * @param {String} color
36805      */
36806     this.relayEvents(ci, ["select"]);
36807 };
36808 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36809  * Based on:
36810  * Ext JS Library 1.1.1
36811  * Copyright(c) 2006-2007, Ext JS, LLC.
36812  *
36813  * Originally Released Under LGPL - original licence link has changed is not relivant.
36814  *
36815  * Fork - LGPL
36816  * <script type="text/javascript">
36817  */
36818  
36819 /**
36820  * @class Roo.form.Field
36821  * @extends Roo.BoxComponent
36822  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36823  * @constructor
36824  * Creates a new Field
36825  * @param {Object} config Configuration options
36826  */
36827 Roo.form.Field = function(config){
36828     Roo.form.Field.superclass.constructor.call(this, config);
36829 };
36830
36831 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36832     /**
36833      * @cfg {String} fieldLabel Label to use when rendering a form.
36834      */
36835        /**
36836      * @cfg {String} qtip Mouse over tip
36837      */
36838      
36839     /**
36840      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36841      */
36842     invalidClass : "x-form-invalid",
36843     /**
36844      * @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")
36845      */
36846     invalidText : "The value in this field is invalid",
36847     /**
36848      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36849      */
36850     focusClass : "x-form-focus",
36851     /**
36852      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36853       automatic validation (defaults to "keyup").
36854      */
36855     validationEvent : "keyup",
36856     /**
36857      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36858      */
36859     validateOnBlur : true,
36860     /**
36861      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36862      */
36863     validationDelay : 250,
36864     /**
36865      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36866      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36867      */
36868     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36869     /**
36870      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36871      */
36872     fieldClass : "x-form-field",
36873     /**
36874      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36875      *<pre>
36876 Value         Description
36877 -----------   ----------------------------------------------------------------------
36878 qtip          Display a quick tip when the user hovers over the field
36879 title         Display a default browser title attribute popup
36880 under         Add a block div beneath the field containing the error text
36881 side          Add an error icon to the right of the field with a popup on hover
36882 [element id]  Add the error text directly to the innerHTML of the specified element
36883 </pre>
36884      */
36885     msgTarget : 'qtip',
36886     /**
36887      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36888      */
36889     msgFx : 'normal',
36890
36891     /**
36892      * @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.
36893      */
36894     readOnly : false,
36895
36896     /**
36897      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36898      */
36899     disabled : false,
36900
36901     /**
36902      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36903      */
36904     inputType : undefined,
36905     
36906     /**
36907      * @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).
36908          */
36909         tabIndex : undefined,
36910         
36911     // private
36912     isFormField : true,
36913
36914     // private
36915     hasFocus : false,
36916     /**
36917      * @property {Roo.Element} fieldEl
36918      * Element Containing the rendered Field (with label etc.)
36919      */
36920     /**
36921      * @cfg {Mixed} value A value to initialize this field with.
36922      */
36923     value : undefined,
36924
36925     /**
36926      * @cfg {String} name The field's HTML name attribute.
36927      */
36928     /**
36929      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36930      */
36931
36932         // private ??
36933         initComponent : function(){
36934         Roo.form.Field.superclass.initComponent.call(this);
36935         this.addEvents({
36936             /**
36937              * @event focus
36938              * Fires when this field receives input focus.
36939              * @param {Roo.form.Field} this
36940              */
36941             focus : true,
36942             /**
36943              * @event blur
36944              * Fires when this field loses input focus.
36945              * @param {Roo.form.Field} this
36946              */
36947             blur : true,
36948             /**
36949              * @event specialkey
36950              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36951              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36952              * @param {Roo.form.Field} this
36953              * @param {Roo.EventObject} e The event object
36954              */
36955             specialkey : true,
36956             /**
36957              * @event change
36958              * Fires just before the field blurs if the field value has changed.
36959              * @param {Roo.form.Field} this
36960              * @param {Mixed} newValue The new value
36961              * @param {Mixed} oldValue The original value
36962              */
36963             change : true,
36964             /**
36965              * @event invalid
36966              * Fires after the field has been marked as invalid.
36967              * @param {Roo.form.Field} this
36968              * @param {String} msg The validation message
36969              */
36970             invalid : true,
36971             /**
36972              * @event valid
36973              * Fires after the field has been validated with no errors.
36974              * @param {Roo.form.Field} this
36975              */
36976             valid : true,
36977              /**
36978              * @event keyup
36979              * Fires after the key up
36980              * @param {Roo.form.Field} this
36981              * @param {Roo.EventObject}  e The event Object
36982              */
36983             keyup : true
36984         });
36985     },
36986
36987     /**
36988      * Returns the name attribute of the field if available
36989      * @return {String} name The field name
36990      */
36991     getName: function(){
36992          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36993     },
36994
36995     // private
36996     onRender : function(ct, position){
36997         Roo.form.Field.superclass.onRender.call(this, ct, position);
36998         if(!this.el){
36999             var cfg = this.getAutoCreate();
37000             if(!cfg.name){
37001                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37002             }
37003             if (!cfg.name.length) {
37004                 delete cfg.name;
37005             }
37006             if(this.inputType){
37007                 cfg.type = this.inputType;
37008             }
37009             this.el = ct.createChild(cfg, position);
37010         }
37011         var type = this.el.dom.type;
37012         if(type){
37013             if(type == 'password'){
37014                 type = 'text';
37015             }
37016             this.el.addClass('x-form-'+type);
37017         }
37018         if(this.readOnly){
37019             this.el.dom.readOnly = true;
37020         }
37021         if(this.tabIndex !== undefined){
37022             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37023         }
37024
37025         this.el.addClass([this.fieldClass, this.cls]);
37026         this.initValue();
37027     },
37028
37029     /**
37030      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37031      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37032      * @return {Roo.form.Field} this
37033      */
37034     applyTo : function(target){
37035         this.allowDomMove = false;
37036         this.el = Roo.get(target);
37037         this.render(this.el.dom.parentNode);
37038         return this;
37039     },
37040
37041     // private
37042     initValue : function(){
37043         if(this.value !== undefined){
37044             this.setValue(this.value);
37045         }else if(this.el.dom.value.length > 0){
37046             this.setValue(this.el.dom.value);
37047         }
37048     },
37049
37050     /**
37051      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37052      */
37053     isDirty : function() {
37054         if(this.disabled) {
37055             return false;
37056         }
37057         return String(this.getValue()) !== String(this.originalValue);
37058     },
37059
37060     // private
37061     afterRender : function(){
37062         Roo.form.Field.superclass.afterRender.call(this);
37063         this.initEvents();
37064     },
37065
37066     // private
37067     fireKey : function(e){
37068         //Roo.log('field ' + e.getKey());
37069         if(e.isNavKeyPress()){
37070             this.fireEvent("specialkey", this, e);
37071         }
37072     },
37073
37074     /**
37075      * Resets the current field value to the originally loaded value and clears any validation messages
37076      */
37077     reset : function(){
37078         this.setValue(this.resetValue);
37079         this.clearInvalid();
37080     },
37081
37082     // private
37083     initEvents : function(){
37084         // safari killled keypress - so keydown is now used..
37085         this.el.on("keydown" , this.fireKey,  this);
37086         this.el.on("focus", this.onFocus,  this);
37087         this.el.on("blur", this.onBlur,  this);
37088         this.el.relayEvent('keyup', this);
37089
37090         // reference to original value for reset
37091         this.originalValue = this.getValue();
37092         this.resetValue =  this.getValue();
37093     },
37094
37095     // private
37096     onFocus : function(){
37097         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37098             this.el.addClass(this.focusClass);
37099         }
37100         if(!this.hasFocus){
37101             this.hasFocus = true;
37102             this.startValue = this.getValue();
37103             this.fireEvent("focus", this);
37104         }
37105     },
37106
37107     beforeBlur : Roo.emptyFn,
37108
37109     // private
37110     onBlur : function(){
37111         this.beforeBlur();
37112         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37113             this.el.removeClass(this.focusClass);
37114         }
37115         this.hasFocus = false;
37116         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37117             this.validate();
37118         }
37119         var v = this.getValue();
37120         if(String(v) !== String(this.startValue)){
37121             this.fireEvent('change', this, v, this.startValue);
37122         }
37123         this.fireEvent("blur", this);
37124     },
37125
37126     /**
37127      * Returns whether or not the field value is currently valid
37128      * @param {Boolean} preventMark True to disable marking the field invalid
37129      * @return {Boolean} True if the value is valid, else false
37130      */
37131     isValid : function(preventMark){
37132         if(this.disabled){
37133             return true;
37134         }
37135         var restore = this.preventMark;
37136         this.preventMark = preventMark === true;
37137         var v = this.validateValue(this.processValue(this.getRawValue()));
37138         this.preventMark = restore;
37139         return v;
37140     },
37141
37142     /**
37143      * Validates the field value
37144      * @return {Boolean} True if the value is valid, else false
37145      */
37146     validate : function(){
37147         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37148             this.clearInvalid();
37149             return true;
37150         }
37151         return false;
37152     },
37153
37154     processValue : function(value){
37155         return value;
37156     },
37157
37158     // private
37159     // Subclasses should provide the validation implementation by overriding this
37160     validateValue : function(value){
37161         return true;
37162     },
37163
37164     /**
37165      * Mark this field as invalid
37166      * @param {String} msg The validation message
37167      */
37168     markInvalid : function(msg){
37169         if(!this.rendered || this.preventMark){ // not rendered
37170             return;
37171         }
37172         
37173         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37174         
37175         obj.el.addClass(this.invalidClass);
37176         msg = msg || this.invalidText;
37177         switch(this.msgTarget){
37178             case 'qtip':
37179                 obj.el.dom.qtip = msg;
37180                 obj.el.dom.qclass = 'x-form-invalid-tip';
37181                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37182                     Roo.QuickTips.enable();
37183                 }
37184                 break;
37185             case 'title':
37186                 this.el.dom.title = msg;
37187                 break;
37188             case 'under':
37189                 if(!this.errorEl){
37190                     var elp = this.el.findParent('.x-form-element', 5, true);
37191                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37192                     this.errorEl.setWidth(elp.getWidth(true)-20);
37193                 }
37194                 this.errorEl.update(msg);
37195                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37196                 break;
37197             case 'side':
37198                 if(!this.errorIcon){
37199                     var elp = this.el.findParent('.x-form-element', 5, true);
37200                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37201                 }
37202                 this.alignErrorIcon();
37203                 this.errorIcon.dom.qtip = msg;
37204                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37205                 this.errorIcon.show();
37206                 this.on('resize', this.alignErrorIcon, this);
37207                 break;
37208             default:
37209                 var t = Roo.getDom(this.msgTarget);
37210                 t.innerHTML = msg;
37211                 t.style.display = this.msgDisplay;
37212                 break;
37213         }
37214         this.fireEvent('invalid', this, msg);
37215     },
37216
37217     // private
37218     alignErrorIcon : function(){
37219         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37220     },
37221
37222     /**
37223      * Clear any invalid styles/messages for this field
37224      */
37225     clearInvalid : function(){
37226         if(!this.rendered || this.preventMark){ // not rendered
37227             return;
37228         }
37229         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37230         
37231         obj.el.removeClass(this.invalidClass);
37232         switch(this.msgTarget){
37233             case 'qtip':
37234                 obj.el.dom.qtip = '';
37235                 break;
37236             case 'title':
37237                 this.el.dom.title = '';
37238                 break;
37239             case 'under':
37240                 if(this.errorEl){
37241                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37242                 }
37243                 break;
37244             case 'side':
37245                 if(this.errorIcon){
37246                     this.errorIcon.dom.qtip = '';
37247                     this.errorIcon.hide();
37248                     this.un('resize', this.alignErrorIcon, this);
37249                 }
37250                 break;
37251             default:
37252                 var t = Roo.getDom(this.msgTarget);
37253                 t.innerHTML = '';
37254                 t.style.display = 'none';
37255                 break;
37256         }
37257         this.fireEvent('valid', this);
37258     },
37259
37260     /**
37261      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37262      * @return {Mixed} value The field value
37263      */
37264     getRawValue : function(){
37265         var v = this.el.getValue();
37266         
37267         return v;
37268     },
37269
37270     /**
37271      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37272      * @return {Mixed} value The field value
37273      */
37274     getValue : function(){
37275         var v = this.el.getValue();
37276          
37277         return v;
37278     },
37279
37280     /**
37281      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37282      * @param {Mixed} value The value to set
37283      */
37284     setRawValue : function(v){
37285         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37286     },
37287
37288     /**
37289      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37290      * @param {Mixed} value The value to set
37291      */
37292     setValue : function(v){
37293         this.value = v;
37294         if(this.rendered){
37295             this.el.dom.value = (v === null || v === undefined ? '' : v);
37296              this.validate();
37297         }
37298     },
37299
37300     adjustSize : function(w, h){
37301         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37302         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37303         return s;
37304     },
37305
37306     adjustWidth : function(tag, w){
37307         tag = tag.toLowerCase();
37308         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37309             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37310                 if(tag == 'input'){
37311                     return w + 2;
37312                 }
37313                 if(tag == 'textarea'){
37314                     return w-2;
37315                 }
37316             }else if(Roo.isOpera){
37317                 if(tag == 'input'){
37318                     return w + 2;
37319                 }
37320                 if(tag == 'textarea'){
37321                     return w-2;
37322                 }
37323             }
37324         }
37325         return w;
37326     }
37327 });
37328
37329
37330 // anything other than normal should be considered experimental
37331 Roo.form.Field.msgFx = {
37332     normal : {
37333         show: function(msgEl, f){
37334             msgEl.setDisplayed('block');
37335         },
37336
37337         hide : function(msgEl, f){
37338             msgEl.setDisplayed(false).update('');
37339         }
37340     },
37341
37342     slide : {
37343         show: function(msgEl, f){
37344             msgEl.slideIn('t', {stopFx:true});
37345         },
37346
37347         hide : function(msgEl, f){
37348             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37349         }
37350     },
37351
37352     slideRight : {
37353         show: function(msgEl, f){
37354             msgEl.fixDisplay();
37355             msgEl.alignTo(f.el, 'tl-tr');
37356             msgEl.slideIn('l', {stopFx:true});
37357         },
37358
37359         hide : function(msgEl, f){
37360             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37361         }
37362     }
37363 };/*
37364  * Based on:
37365  * Ext JS Library 1.1.1
37366  * Copyright(c) 2006-2007, Ext JS, LLC.
37367  *
37368  * Originally Released Under LGPL - original licence link has changed is not relivant.
37369  *
37370  * Fork - LGPL
37371  * <script type="text/javascript">
37372  */
37373  
37374
37375 /**
37376  * @class Roo.form.TextField
37377  * @extends Roo.form.Field
37378  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37379  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37380  * @constructor
37381  * Creates a new TextField
37382  * @param {Object} config Configuration options
37383  */
37384 Roo.form.TextField = function(config){
37385     Roo.form.TextField.superclass.constructor.call(this, config);
37386     this.addEvents({
37387         /**
37388          * @event autosize
37389          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37390          * according to the default logic, but this event provides a hook for the developer to apply additional
37391          * logic at runtime to resize the field if needed.
37392              * @param {Roo.form.Field} this This text field
37393              * @param {Number} width The new field width
37394              */
37395         autosize : true
37396     });
37397 };
37398
37399 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37400     /**
37401      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37402      */
37403     grow : false,
37404     /**
37405      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37406      */
37407     growMin : 30,
37408     /**
37409      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37410      */
37411     growMax : 800,
37412     /**
37413      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37414      */
37415     vtype : null,
37416     /**
37417      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37418      */
37419     maskRe : null,
37420     /**
37421      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37422      */
37423     disableKeyFilter : false,
37424     /**
37425      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37426      */
37427     allowBlank : true,
37428     /**
37429      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37430      */
37431     minLength : 0,
37432     /**
37433      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37434      */
37435     maxLength : Number.MAX_VALUE,
37436     /**
37437      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37438      */
37439     minLengthText : "The minimum length for this field is {0}",
37440     /**
37441      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37442      */
37443     maxLengthText : "The maximum length for this field is {0}",
37444     /**
37445      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37446      */
37447     selectOnFocus : false,
37448     /**
37449      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37450      */
37451     blankText : "This field is required",
37452     /**
37453      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37454      * If available, this function will be called only after the basic validators all return true, and will be passed the
37455      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37456      */
37457     validator : null,
37458     /**
37459      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37460      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37461      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37462      */
37463     regex : null,
37464     /**
37465      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37466      */
37467     regexText : "",
37468     /**
37469      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37470      */
37471     emptyText : null,
37472    
37473
37474     // private
37475     initEvents : function()
37476     {
37477         if (this.emptyText) {
37478             this.el.attr('placeholder', this.emptyText);
37479         }
37480         
37481         Roo.form.TextField.superclass.initEvents.call(this);
37482         if(this.validationEvent == 'keyup'){
37483             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37484             this.el.on('keyup', this.filterValidation, this);
37485         }
37486         else if(this.validationEvent !== false){
37487             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37488         }
37489         
37490         if(this.selectOnFocus){
37491             this.on("focus", this.preFocus, this);
37492             
37493         }
37494         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37495             this.el.on("keypress", this.filterKeys, this);
37496         }
37497         if(this.grow){
37498             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37499             this.el.on("click", this.autoSize,  this);
37500         }
37501         if(this.el.is('input[type=password]') && Roo.isSafari){
37502             this.el.on('keydown', this.SafariOnKeyDown, this);
37503         }
37504     },
37505
37506     processValue : function(value){
37507         if(this.stripCharsRe){
37508             var newValue = value.replace(this.stripCharsRe, '');
37509             if(newValue !== value){
37510                 this.setRawValue(newValue);
37511                 return newValue;
37512             }
37513         }
37514         return value;
37515     },
37516
37517     filterValidation : function(e){
37518         if(!e.isNavKeyPress()){
37519             this.validationTask.delay(this.validationDelay);
37520         }
37521     },
37522
37523     // private
37524     onKeyUp : function(e){
37525         if(!e.isNavKeyPress()){
37526             this.autoSize();
37527         }
37528     },
37529
37530     /**
37531      * Resets the current field value to the originally-loaded value and clears any validation messages.
37532      *  
37533      */
37534     reset : function(){
37535         Roo.form.TextField.superclass.reset.call(this);
37536        
37537     },
37538
37539     
37540     // private
37541     preFocus : function(){
37542         
37543         if(this.selectOnFocus){
37544             this.el.dom.select();
37545         }
37546     },
37547
37548     
37549     // private
37550     filterKeys : function(e){
37551         var k = e.getKey();
37552         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37553             return;
37554         }
37555         var c = e.getCharCode(), cc = String.fromCharCode(c);
37556         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37557             return;
37558         }
37559         if(!this.maskRe.test(cc)){
37560             e.stopEvent();
37561         }
37562     },
37563
37564     setValue : function(v){
37565         
37566         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37567         
37568         this.autoSize();
37569     },
37570
37571     /**
37572      * Validates a value according to the field's validation rules and marks the field as invalid
37573      * if the validation fails
37574      * @param {Mixed} value The value to validate
37575      * @return {Boolean} True if the value is valid, else false
37576      */
37577     validateValue : function(value){
37578         if(value.length < 1)  { // if it's blank
37579              if(this.allowBlank){
37580                 this.clearInvalid();
37581                 return true;
37582              }else{
37583                 this.markInvalid(this.blankText);
37584                 return false;
37585              }
37586         }
37587         if(value.length < this.minLength){
37588             this.markInvalid(String.format(this.minLengthText, this.minLength));
37589             return false;
37590         }
37591         if(value.length > this.maxLength){
37592             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37593             return false;
37594         }
37595         if(this.vtype){
37596             var vt = Roo.form.VTypes;
37597             if(!vt[this.vtype](value, this)){
37598                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37599                 return false;
37600             }
37601         }
37602         if(typeof this.validator == "function"){
37603             var msg = this.validator(value);
37604             if(msg !== true){
37605                 this.markInvalid(msg);
37606                 return false;
37607             }
37608         }
37609         if(this.regex && !this.regex.test(value)){
37610             this.markInvalid(this.regexText);
37611             return false;
37612         }
37613         return true;
37614     },
37615
37616     /**
37617      * Selects text in this field
37618      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37619      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37620      */
37621     selectText : function(start, end){
37622         var v = this.getRawValue();
37623         if(v.length > 0){
37624             start = start === undefined ? 0 : start;
37625             end = end === undefined ? v.length : end;
37626             var d = this.el.dom;
37627             if(d.setSelectionRange){
37628                 d.setSelectionRange(start, end);
37629             }else if(d.createTextRange){
37630                 var range = d.createTextRange();
37631                 range.moveStart("character", start);
37632                 range.moveEnd("character", v.length-end);
37633                 range.select();
37634             }
37635         }
37636     },
37637
37638     /**
37639      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37640      * This only takes effect if grow = true, and fires the autosize event.
37641      */
37642     autoSize : function(){
37643         if(!this.grow || !this.rendered){
37644             return;
37645         }
37646         if(!this.metrics){
37647             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37648         }
37649         var el = this.el;
37650         var v = el.dom.value;
37651         var d = document.createElement('div');
37652         d.appendChild(document.createTextNode(v));
37653         v = d.innerHTML;
37654         d = null;
37655         v += "&#160;";
37656         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37657         this.el.setWidth(w);
37658         this.fireEvent("autosize", this, w);
37659     },
37660     
37661     // private
37662     SafariOnKeyDown : function(event)
37663     {
37664         // this is a workaround for a password hang bug on chrome/ webkit.
37665         
37666         var isSelectAll = false;
37667         
37668         if(this.el.dom.selectionEnd > 0){
37669             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37670         }
37671         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37672             event.preventDefault();
37673             this.setValue('');
37674             return;
37675         }
37676         
37677         if(isSelectAll){ // backspace and delete key
37678             
37679             event.preventDefault();
37680             // this is very hacky as keydown always get's upper case.
37681             //
37682             var cc = String.fromCharCode(event.getCharCode());
37683             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37684             
37685         }
37686         
37687         
37688     }
37689 });/*
37690  * Based on:
37691  * Ext JS Library 1.1.1
37692  * Copyright(c) 2006-2007, Ext JS, LLC.
37693  *
37694  * Originally Released Under LGPL - original licence link has changed is not relivant.
37695  *
37696  * Fork - LGPL
37697  * <script type="text/javascript">
37698  */
37699  
37700 /**
37701  * @class Roo.form.Hidden
37702  * @extends Roo.form.TextField
37703  * Simple Hidden element used on forms 
37704  * 
37705  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37706  * 
37707  * @constructor
37708  * Creates a new Hidden form element.
37709  * @param {Object} config Configuration options
37710  */
37711
37712
37713
37714 // easy hidden field...
37715 Roo.form.Hidden = function(config){
37716     Roo.form.Hidden.superclass.constructor.call(this, config);
37717 };
37718   
37719 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37720     fieldLabel:      '',
37721     inputType:      'hidden',
37722     width:          50,
37723     allowBlank:     true,
37724     labelSeparator: '',
37725     hidden:         true,
37726     itemCls :       'x-form-item-display-none'
37727
37728
37729 });
37730
37731
37732 /*
37733  * Based on:
37734  * Ext JS Library 1.1.1
37735  * Copyright(c) 2006-2007, Ext JS, LLC.
37736  *
37737  * Originally Released Under LGPL - original licence link has changed is not relivant.
37738  *
37739  * Fork - LGPL
37740  * <script type="text/javascript">
37741  */
37742  
37743 /**
37744  * @class Roo.form.TriggerField
37745  * @extends Roo.form.TextField
37746  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37747  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37748  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37749  * for which you can provide a custom implementation.  For example:
37750  * <pre><code>
37751 var trigger = new Roo.form.TriggerField();
37752 trigger.onTriggerClick = myTriggerFn;
37753 trigger.applyTo('my-field');
37754 </code></pre>
37755  *
37756  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37757  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37758  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37759  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37760  * @constructor
37761  * Create a new TriggerField.
37762  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37763  * to the base TextField)
37764  */
37765 Roo.form.TriggerField = function(config){
37766     this.mimicing = false;
37767     Roo.form.TriggerField.superclass.constructor.call(this, config);
37768 };
37769
37770 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37771     /**
37772      * @cfg {String} triggerClass A CSS class to apply to the trigger
37773      */
37774     /**
37775      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37776      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37777      */
37778     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37779     /**
37780      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37781      */
37782     hideTrigger:false,
37783
37784     /** @cfg {Boolean} grow @hide */
37785     /** @cfg {Number} growMin @hide */
37786     /** @cfg {Number} growMax @hide */
37787
37788     /**
37789      * @hide 
37790      * @method
37791      */
37792     autoSize: Roo.emptyFn,
37793     // private
37794     monitorTab : true,
37795     // private
37796     deferHeight : true,
37797
37798     
37799     actionMode : 'wrap',
37800     // private
37801     onResize : function(w, h){
37802         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37803         if(typeof w == 'number'){
37804             var x = w - this.trigger.getWidth();
37805             this.el.setWidth(this.adjustWidth('input', x));
37806             this.trigger.setStyle('left', x+'px');
37807         }
37808     },
37809
37810     // private
37811     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37812
37813     // private
37814     getResizeEl : function(){
37815         return this.wrap;
37816     },
37817
37818     // private
37819     getPositionEl : function(){
37820         return this.wrap;
37821     },
37822
37823     // private
37824     alignErrorIcon : function(){
37825         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37826     },
37827
37828     // private
37829     onRender : function(ct, position){
37830         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37831         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37832         this.trigger = this.wrap.createChild(this.triggerConfig ||
37833                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37834         if(this.hideTrigger){
37835             this.trigger.setDisplayed(false);
37836         }
37837         this.initTrigger();
37838         if(!this.width){
37839             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37840         }
37841     },
37842
37843     // private
37844     initTrigger : function(){
37845         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37846         this.trigger.addClassOnOver('x-form-trigger-over');
37847         this.trigger.addClassOnClick('x-form-trigger-click');
37848     },
37849
37850     // private
37851     onDestroy : function(){
37852         if(this.trigger){
37853             this.trigger.removeAllListeners();
37854             this.trigger.remove();
37855         }
37856         if(this.wrap){
37857             this.wrap.remove();
37858         }
37859         Roo.form.TriggerField.superclass.onDestroy.call(this);
37860     },
37861
37862     // private
37863     onFocus : function(){
37864         Roo.form.TriggerField.superclass.onFocus.call(this);
37865         if(!this.mimicing){
37866             this.wrap.addClass('x-trigger-wrap-focus');
37867             this.mimicing = true;
37868             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37869             if(this.monitorTab){
37870                 this.el.on("keydown", this.checkTab, this);
37871             }
37872         }
37873     },
37874
37875     // private
37876     checkTab : function(e){
37877         if(e.getKey() == e.TAB){
37878             this.triggerBlur();
37879         }
37880     },
37881
37882     // private
37883     onBlur : function(){
37884         // do nothing
37885     },
37886
37887     // private
37888     mimicBlur : function(e, t){
37889         if(!this.wrap.contains(t) && this.validateBlur()){
37890             this.triggerBlur();
37891         }
37892     },
37893
37894     // private
37895     triggerBlur : function(){
37896         this.mimicing = false;
37897         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37898         if(this.monitorTab){
37899             this.el.un("keydown", this.checkTab, this);
37900         }
37901         this.wrap.removeClass('x-trigger-wrap-focus');
37902         Roo.form.TriggerField.superclass.onBlur.call(this);
37903     },
37904
37905     // private
37906     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37907     validateBlur : function(e, t){
37908         return true;
37909     },
37910
37911     // private
37912     onDisable : function(){
37913         Roo.form.TriggerField.superclass.onDisable.call(this);
37914         if(this.wrap){
37915             this.wrap.addClass('x-item-disabled');
37916         }
37917     },
37918
37919     // private
37920     onEnable : function(){
37921         Roo.form.TriggerField.superclass.onEnable.call(this);
37922         if(this.wrap){
37923             this.wrap.removeClass('x-item-disabled');
37924         }
37925     },
37926
37927     // private
37928     onShow : function(){
37929         var ae = this.getActionEl();
37930         
37931         if(ae){
37932             ae.dom.style.display = '';
37933             ae.dom.style.visibility = 'visible';
37934         }
37935     },
37936
37937     // private
37938     
37939     onHide : function(){
37940         var ae = this.getActionEl();
37941         ae.dom.style.display = 'none';
37942     },
37943
37944     /**
37945      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37946      * by an implementing function.
37947      * @method
37948      * @param {EventObject} e
37949      */
37950     onTriggerClick : Roo.emptyFn
37951 });
37952
37953 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37954 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37955 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37956 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37957     initComponent : function(){
37958         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37959
37960         this.triggerConfig = {
37961             tag:'span', cls:'x-form-twin-triggers', cn:[
37962             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37963             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37964         ]};
37965     },
37966
37967     getTrigger : function(index){
37968         return this.triggers[index];
37969     },
37970
37971     initTrigger : function(){
37972         var ts = this.trigger.select('.x-form-trigger', true);
37973         this.wrap.setStyle('overflow', 'hidden');
37974         var triggerField = this;
37975         ts.each(function(t, all, index){
37976             t.hide = function(){
37977                 var w = triggerField.wrap.getWidth();
37978                 this.dom.style.display = 'none';
37979                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37980             };
37981             t.show = function(){
37982                 var w = triggerField.wrap.getWidth();
37983                 this.dom.style.display = '';
37984                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37985             };
37986             var triggerIndex = 'Trigger'+(index+1);
37987
37988             if(this['hide'+triggerIndex]){
37989                 t.dom.style.display = 'none';
37990             }
37991             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37992             t.addClassOnOver('x-form-trigger-over');
37993             t.addClassOnClick('x-form-trigger-click');
37994         }, this);
37995         this.triggers = ts.elements;
37996     },
37997
37998     onTrigger1Click : Roo.emptyFn,
37999     onTrigger2Click : Roo.emptyFn
38000 });/*
38001  * Based on:
38002  * Ext JS Library 1.1.1
38003  * Copyright(c) 2006-2007, Ext JS, LLC.
38004  *
38005  * Originally Released Under LGPL - original licence link has changed is not relivant.
38006  *
38007  * Fork - LGPL
38008  * <script type="text/javascript">
38009  */
38010  
38011 /**
38012  * @class Roo.form.TextArea
38013  * @extends Roo.form.TextField
38014  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38015  * support for auto-sizing.
38016  * @constructor
38017  * Creates a new TextArea
38018  * @param {Object} config Configuration options
38019  */
38020 Roo.form.TextArea = function(config){
38021     Roo.form.TextArea.superclass.constructor.call(this, config);
38022     // these are provided exchanges for backwards compat
38023     // minHeight/maxHeight were replaced by growMin/growMax to be
38024     // compatible with TextField growing config values
38025     if(this.minHeight !== undefined){
38026         this.growMin = this.minHeight;
38027     }
38028     if(this.maxHeight !== undefined){
38029         this.growMax = this.maxHeight;
38030     }
38031 };
38032
38033 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38034     /**
38035      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38036      */
38037     growMin : 60,
38038     /**
38039      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38040      */
38041     growMax: 1000,
38042     /**
38043      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38044      * in the field (equivalent to setting overflow: hidden, defaults to false)
38045      */
38046     preventScrollbars: false,
38047     /**
38048      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38049      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38050      */
38051
38052     // private
38053     onRender : function(ct, position){
38054         if(!this.el){
38055             this.defaultAutoCreate = {
38056                 tag: "textarea",
38057                 style:"width:300px;height:60px;",
38058                 autocomplete: "off"
38059             };
38060         }
38061         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38062         if(this.grow){
38063             this.textSizeEl = Roo.DomHelper.append(document.body, {
38064                 tag: "pre", cls: "x-form-grow-sizer"
38065             });
38066             if(this.preventScrollbars){
38067                 this.el.setStyle("overflow", "hidden");
38068             }
38069             this.el.setHeight(this.growMin);
38070         }
38071     },
38072
38073     onDestroy : function(){
38074         if(this.textSizeEl){
38075             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38076         }
38077         Roo.form.TextArea.superclass.onDestroy.call(this);
38078     },
38079
38080     // private
38081     onKeyUp : function(e){
38082         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38083             this.autoSize();
38084         }
38085     },
38086
38087     /**
38088      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38089      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38090      */
38091     autoSize : function(){
38092         if(!this.grow || !this.textSizeEl){
38093             return;
38094         }
38095         var el = this.el;
38096         var v = el.dom.value;
38097         var ts = this.textSizeEl;
38098
38099         ts.innerHTML = '';
38100         ts.appendChild(document.createTextNode(v));
38101         v = ts.innerHTML;
38102
38103         Roo.fly(ts).setWidth(this.el.getWidth());
38104         if(v.length < 1){
38105             v = "&#160;&#160;";
38106         }else{
38107             if(Roo.isIE){
38108                 v = v.replace(/\n/g, '<p>&#160;</p>');
38109             }
38110             v += "&#160;\n&#160;";
38111         }
38112         ts.innerHTML = v;
38113         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38114         if(h != this.lastHeight){
38115             this.lastHeight = h;
38116             this.el.setHeight(h);
38117             this.fireEvent("autosize", this, h);
38118         }
38119     }
38120 });/*
38121  * Based on:
38122  * Ext JS Library 1.1.1
38123  * Copyright(c) 2006-2007, Ext JS, LLC.
38124  *
38125  * Originally Released Under LGPL - original licence link has changed is not relivant.
38126  *
38127  * Fork - LGPL
38128  * <script type="text/javascript">
38129  */
38130  
38131
38132 /**
38133  * @class Roo.form.NumberField
38134  * @extends Roo.form.TextField
38135  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38136  * @constructor
38137  * Creates a new NumberField
38138  * @param {Object} config Configuration options
38139  */
38140 Roo.form.NumberField = function(config){
38141     Roo.form.NumberField.superclass.constructor.call(this, config);
38142 };
38143
38144 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38145     /**
38146      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38147      */
38148     fieldClass: "x-form-field x-form-num-field",
38149     /**
38150      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38151      */
38152     allowDecimals : true,
38153     /**
38154      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38155      */
38156     decimalSeparator : ".",
38157     /**
38158      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38159      */
38160     decimalPrecision : 2,
38161     /**
38162      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38163      */
38164     allowNegative : true,
38165     /**
38166      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38167      */
38168     minValue : Number.NEGATIVE_INFINITY,
38169     /**
38170      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38171      */
38172     maxValue : Number.MAX_VALUE,
38173     /**
38174      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38175      */
38176     minText : "The minimum value for this field is {0}",
38177     /**
38178      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38179      */
38180     maxText : "The maximum value for this field is {0}",
38181     /**
38182      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38183      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38184      */
38185     nanText : "{0} is not a valid number",
38186
38187     // private
38188     initEvents : function(){
38189         Roo.form.NumberField.superclass.initEvents.call(this);
38190         var allowed = "0123456789";
38191         if(this.allowDecimals){
38192             allowed += this.decimalSeparator;
38193         }
38194         if(this.allowNegative){
38195             allowed += "-";
38196         }
38197         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38198         var keyPress = function(e){
38199             var k = e.getKey();
38200             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38201                 return;
38202             }
38203             var c = e.getCharCode();
38204             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38205                 e.stopEvent();
38206             }
38207         };
38208         this.el.on("keypress", keyPress, this);
38209     },
38210
38211     // private
38212     validateValue : function(value){
38213         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38214             return false;
38215         }
38216         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38217              return true;
38218         }
38219         var num = this.parseValue(value);
38220         if(isNaN(num)){
38221             this.markInvalid(String.format(this.nanText, value));
38222             return false;
38223         }
38224         if(num < this.minValue){
38225             this.markInvalid(String.format(this.minText, this.minValue));
38226             return false;
38227         }
38228         if(num > this.maxValue){
38229             this.markInvalid(String.format(this.maxText, this.maxValue));
38230             return false;
38231         }
38232         return true;
38233     },
38234
38235     getValue : function(){
38236         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38237     },
38238
38239     // private
38240     parseValue : function(value){
38241         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38242         return isNaN(value) ? '' : value;
38243     },
38244
38245     // private
38246     fixPrecision : function(value){
38247         var nan = isNaN(value);
38248         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38249             return nan ? '' : value;
38250         }
38251         return parseFloat(value).toFixed(this.decimalPrecision);
38252     },
38253
38254     setValue : function(v){
38255         v = this.fixPrecision(v);
38256         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38257     },
38258
38259     // private
38260     decimalPrecisionFcn : function(v){
38261         return Math.floor(v);
38262     },
38263
38264     beforeBlur : function(){
38265         var v = this.parseValue(this.getRawValue());
38266         if(v){
38267             this.setValue(v);
38268         }
38269     }
38270 });/*
38271  * Based on:
38272  * Ext JS Library 1.1.1
38273  * Copyright(c) 2006-2007, Ext JS, LLC.
38274  *
38275  * Originally Released Under LGPL - original licence link has changed is not relivant.
38276  *
38277  * Fork - LGPL
38278  * <script type="text/javascript">
38279  */
38280  
38281 /**
38282  * @class Roo.form.DateField
38283  * @extends Roo.form.TriggerField
38284  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38285 * @constructor
38286 * Create a new DateField
38287 * @param {Object} config
38288  */
38289 Roo.form.DateField = function(config){
38290     Roo.form.DateField.superclass.constructor.call(this, config);
38291     
38292       this.addEvents({
38293          
38294         /**
38295          * @event select
38296          * Fires when a date is selected
38297              * @param {Roo.form.DateField} combo This combo box
38298              * @param {Date} date The date selected
38299              */
38300         'select' : true
38301          
38302     });
38303     
38304     
38305     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38306     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38307     this.ddMatch = null;
38308     if(this.disabledDates){
38309         var dd = this.disabledDates;
38310         var re = "(?:";
38311         for(var i = 0; i < dd.length; i++){
38312             re += dd[i];
38313             if(i != dd.length-1) re += "|";
38314         }
38315         this.ddMatch = new RegExp(re + ")");
38316     }
38317 };
38318
38319 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38320     /**
38321      * @cfg {String} format
38322      * The default date format string which can be overriden for localization support.  The format must be
38323      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38324      */
38325     format : "m/d/y",
38326     /**
38327      * @cfg {String} altFormats
38328      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38329      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38330      */
38331     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38332     /**
38333      * @cfg {Array} disabledDays
38334      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38335      */
38336     disabledDays : null,
38337     /**
38338      * @cfg {String} disabledDaysText
38339      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38340      */
38341     disabledDaysText : "Disabled",
38342     /**
38343      * @cfg {Array} disabledDates
38344      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38345      * expression so they are very powerful. Some examples:
38346      * <ul>
38347      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38348      * <li>["03/08", "09/16"] would disable those days for every year</li>
38349      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38350      * <li>["03/../2006"] would disable every day in March 2006</li>
38351      * <li>["^03"] would disable every day in every March</li>
38352      * </ul>
38353      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38354      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38355      */
38356     disabledDates : null,
38357     /**
38358      * @cfg {String} disabledDatesText
38359      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38360      */
38361     disabledDatesText : "Disabled",
38362     /**
38363      * @cfg {Date/String} minValue
38364      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38365      * valid format (defaults to null).
38366      */
38367     minValue : null,
38368     /**
38369      * @cfg {Date/String} maxValue
38370      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38371      * valid format (defaults to null).
38372      */
38373     maxValue : null,
38374     /**
38375      * @cfg {String} minText
38376      * The error text to display when the date in the cell is before minValue (defaults to
38377      * 'The date in this field must be after {minValue}').
38378      */
38379     minText : "The date in this field must be equal to or after {0}",
38380     /**
38381      * @cfg {String} maxText
38382      * The error text to display when the date in the cell is after maxValue (defaults to
38383      * 'The date in this field must be before {maxValue}').
38384      */
38385     maxText : "The date in this field must be equal to or before {0}",
38386     /**
38387      * @cfg {String} invalidText
38388      * The error text to display when the date in the field is invalid (defaults to
38389      * '{value} is not a valid date - it must be in the format {format}').
38390      */
38391     invalidText : "{0} is not a valid date - it must be in the format {1}",
38392     /**
38393      * @cfg {String} triggerClass
38394      * An additional CSS class used to style the trigger button.  The trigger will always get the
38395      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38396      * which displays a calendar icon).
38397      */
38398     triggerClass : 'x-form-date-trigger',
38399     
38400
38401     /**
38402      * @cfg {Boolean} useIso
38403      * if enabled, then the date field will use a hidden field to store the 
38404      * real value as iso formated date. default (false)
38405      */ 
38406     useIso : false,
38407     /**
38408      * @cfg {String/Object} autoCreate
38409      * A DomHelper element spec, or true for a default element spec (defaults to
38410      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38411      */ 
38412     // private
38413     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38414     
38415     // private
38416     hiddenField: false,
38417     
38418     onRender : function(ct, position)
38419     {
38420         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38421         if (this.useIso) {
38422             //this.el.dom.removeAttribute('name'); 
38423             Roo.log("Changing name?");
38424             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38425             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38426                     'before', true);
38427             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38428             // prevent input submission
38429             this.hiddenName = this.name;
38430         }
38431             
38432             
38433     },
38434     
38435     // private
38436     validateValue : function(value)
38437     {
38438         value = this.formatDate(value);
38439         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38440             Roo.log('super failed');
38441             return false;
38442         }
38443         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38444              return true;
38445         }
38446         var svalue = value;
38447         value = this.parseDate(value);
38448         if(!value){
38449             Roo.log('parse date failed' + svalue);
38450             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38451             return false;
38452         }
38453         var time = value.getTime();
38454         if(this.minValue && time < this.minValue.getTime()){
38455             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38456             return false;
38457         }
38458         if(this.maxValue && time > this.maxValue.getTime()){
38459             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38460             return false;
38461         }
38462         if(this.disabledDays){
38463             var day = value.getDay();
38464             for(var i = 0; i < this.disabledDays.length; i++) {
38465                 if(day === this.disabledDays[i]){
38466                     this.markInvalid(this.disabledDaysText);
38467                     return false;
38468                 }
38469             }
38470         }
38471         var fvalue = this.formatDate(value);
38472         if(this.ddMatch && this.ddMatch.test(fvalue)){
38473             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38474             return false;
38475         }
38476         return true;
38477     },
38478
38479     // private
38480     // Provides logic to override the default TriggerField.validateBlur which just returns true
38481     validateBlur : function(){
38482         return !this.menu || !this.menu.isVisible();
38483     },
38484     
38485     getName: function()
38486     {
38487         // returns hidden if it's set..
38488         if (!this.rendered) {return ''};
38489         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38490         
38491     },
38492
38493     /**
38494      * Returns the current date value of the date field.
38495      * @return {Date} The date value
38496      */
38497     getValue : function(){
38498         
38499         return  this.hiddenField ?
38500                 this.hiddenField.value :
38501                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38502     },
38503
38504     /**
38505      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38506      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38507      * (the default format used is "m/d/y").
38508      * <br />Usage:
38509      * <pre><code>
38510 //All of these calls set the same date value (May 4, 2006)
38511
38512 //Pass a date object:
38513 var dt = new Date('5/4/06');
38514 dateField.setValue(dt);
38515
38516 //Pass a date string (default format):
38517 dateField.setValue('5/4/06');
38518
38519 //Pass a date string (custom format):
38520 dateField.format = 'Y-m-d';
38521 dateField.setValue('2006-5-4');
38522 </code></pre>
38523      * @param {String/Date} date The date or valid date string
38524      */
38525     setValue : function(date){
38526         if (this.hiddenField) {
38527             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38528         }
38529         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38530         // make sure the value field is always stored as a date..
38531         this.value = this.parseDate(date);
38532         
38533         
38534     },
38535
38536     // private
38537     parseDate : function(value){
38538         if(!value || value instanceof Date){
38539             return value;
38540         }
38541         var v = Date.parseDate(value, this.format);
38542          if (!v && this.useIso) {
38543             v = Date.parseDate(value, 'Y-m-d');
38544         }
38545         if(!v && this.altFormats){
38546             if(!this.altFormatsArray){
38547                 this.altFormatsArray = this.altFormats.split("|");
38548             }
38549             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38550                 v = Date.parseDate(value, this.altFormatsArray[i]);
38551             }
38552         }
38553         return v;
38554     },
38555
38556     // private
38557     formatDate : function(date, fmt){
38558         return (!date || !(date instanceof Date)) ?
38559                date : date.dateFormat(fmt || this.format);
38560     },
38561
38562     // private
38563     menuListeners : {
38564         select: function(m, d){
38565             
38566             this.setValue(d);
38567             this.fireEvent('select', this, d);
38568         },
38569         show : function(){ // retain focus styling
38570             this.onFocus();
38571         },
38572         hide : function(){
38573             this.focus.defer(10, this);
38574             var ml = this.menuListeners;
38575             this.menu.un("select", ml.select,  this);
38576             this.menu.un("show", ml.show,  this);
38577             this.menu.un("hide", ml.hide,  this);
38578         }
38579     },
38580
38581     // private
38582     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38583     onTriggerClick : function(){
38584         if(this.disabled){
38585             return;
38586         }
38587         if(this.menu == null){
38588             this.menu = new Roo.menu.DateMenu();
38589         }
38590         Roo.apply(this.menu.picker,  {
38591             showClear: this.allowBlank,
38592             minDate : this.minValue,
38593             maxDate : this.maxValue,
38594             disabledDatesRE : this.ddMatch,
38595             disabledDatesText : this.disabledDatesText,
38596             disabledDays : this.disabledDays,
38597             disabledDaysText : this.disabledDaysText,
38598             format : this.useIso ? 'Y-m-d' : this.format,
38599             minText : String.format(this.minText, this.formatDate(this.minValue)),
38600             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38601         });
38602         this.menu.on(Roo.apply({}, this.menuListeners, {
38603             scope:this
38604         }));
38605         this.menu.picker.setValue(this.getValue() || new Date());
38606         this.menu.show(this.el, "tl-bl?");
38607     },
38608
38609     beforeBlur : function(){
38610         var v = this.parseDate(this.getRawValue());
38611         if(v){
38612             this.setValue(v);
38613         }
38614     },
38615
38616     /*@
38617      * overide
38618      * 
38619      */
38620     isDirty : function() {
38621         if(this.disabled) {
38622             return false;
38623         }
38624         
38625         if(typeof(this.startValue) === 'undefined'){
38626             return false;
38627         }
38628         
38629         return String(this.getValue()) !== String(this.startValue);
38630         
38631     }
38632 });/*
38633  * Based on:
38634  * Ext JS Library 1.1.1
38635  * Copyright(c) 2006-2007, Ext JS, LLC.
38636  *
38637  * Originally Released Under LGPL - original licence link has changed is not relivant.
38638  *
38639  * Fork - LGPL
38640  * <script type="text/javascript">
38641  */
38642  
38643 /**
38644  * @class Roo.form.MonthField
38645  * @extends Roo.form.TriggerField
38646  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38647 * @constructor
38648 * Create a new MonthField
38649 * @param {Object} config
38650  */
38651 Roo.form.MonthField = function(config){
38652     
38653     Roo.form.MonthField.superclass.constructor.call(this, config);
38654     
38655       this.addEvents({
38656          
38657         /**
38658          * @event select
38659          * Fires when a date is selected
38660              * @param {Roo.form.MonthFieeld} combo This combo box
38661              * @param {Date} date The date selected
38662              */
38663         'select' : true
38664          
38665     });
38666     
38667     
38668     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38669     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38670     this.ddMatch = null;
38671     if(this.disabledDates){
38672         var dd = this.disabledDates;
38673         var re = "(?:";
38674         for(var i = 0; i < dd.length; i++){
38675             re += dd[i];
38676             if(i != dd.length-1) re += "|";
38677         }
38678         this.ddMatch = new RegExp(re + ")");
38679     }
38680 };
38681
38682 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38683     /**
38684      * @cfg {String} format
38685      * The default date format string which can be overriden for localization support.  The format must be
38686      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38687      */
38688     format : "M Y",
38689     /**
38690      * @cfg {String} altFormats
38691      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38692      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38693      */
38694     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38695     /**
38696      * @cfg {Array} disabledDays
38697      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38698      */
38699     disabledDays : [0,1,2,3,4,5,6],
38700     /**
38701      * @cfg {String} disabledDaysText
38702      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38703      */
38704     disabledDaysText : "Disabled",
38705     /**
38706      * @cfg {Array} disabledDates
38707      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38708      * expression so they are very powerful. Some examples:
38709      * <ul>
38710      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38711      * <li>["03/08", "09/16"] would disable those days for every year</li>
38712      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38713      * <li>["03/../2006"] would disable every day in March 2006</li>
38714      * <li>["^03"] would disable every day in every March</li>
38715      * </ul>
38716      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38717      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38718      */
38719     disabledDates : null,
38720     /**
38721      * @cfg {String} disabledDatesText
38722      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38723      */
38724     disabledDatesText : "Disabled",
38725     /**
38726      * @cfg {Date/String} minValue
38727      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38728      * valid format (defaults to null).
38729      */
38730     minValue : null,
38731     /**
38732      * @cfg {Date/String} maxValue
38733      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38734      * valid format (defaults to null).
38735      */
38736     maxValue : null,
38737     /**
38738      * @cfg {String} minText
38739      * The error text to display when the date in the cell is before minValue (defaults to
38740      * 'The date in this field must be after {minValue}').
38741      */
38742     minText : "The date in this field must be equal to or after {0}",
38743     /**
38744      * @cfg {String} maxTextf
38745      * The error text to display when the date in the cell is after maxValue (defaults to
38746      * 'The date in this field must be before {maxValue}').
38747      */
38748     maxText : "The date in this field must be equal to or before {0}",
38749     /**
38750      * @cfg {String} invalidText
38751      * The error text to display when the date in the field is invalid (defaults to
38752      * '{value} is not a valid date - it must be in the format {format}').
38753      */
38754     invalidText : "{0} is not a valid date - it must be in the format {1}",
38755     /**
38756      * @cfg {String} triggerClass
38757      * An additional CSS class used to style the trigger button.  The trigger will always get the
38758      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38759      * which displays a calendar icon).
38760      */
38761     triggerClass : 'x-form-date-trigger',
38762     
38763
38764     /**
38765      * @cfg {Boolean} useIso
38766      * if enabled, then the date field will use a hidden field to store the 
38767      * real value as iso formated date. default (true)
38768      */ 
38769     useIso : true,
38770     /**
38771      * @cfg {String/Object} autoCreate
38772      * A DomHelper element spec, or true for a default element spec (defaults to
38773      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38774      */ 
38775     // private
38776     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38777     
38778     // private
38779     hiddenField: false,
38780     
38781     hideMonthPicker : false,
38782     
38783     onRender : function(ct, position)
38784     {
38785         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38786         if (this.useIso) {
38787             this.el.dom.removeAttribute('name'); 
38788             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38789                     'before', true);
38790             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38791             // prevent input submission
38792             this.hiddenName = this.name;
38793         }
38794             
38795             
38796     },
38797     
38798     // private
38799     validateValue : function(value)
38800     {
38801         value = this.formatDate(value);
38802         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38803             return false;
38804         }
38805         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38806              return true;
38807         }
38808         var svalue = value;
38809         value = this.parseDate(value);
38810         if(!value){
38811             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38812             return false;
38813         }
38814         var time = value.getTime();
38815         if(this.minValue && time < this.minValue.getTime()){
38816             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38817             return false;
38818         }
38819         if(this.maxValue && time > this.maxValue.getTime()){
38820             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38821             return false;
38822         }
38823         /*if(this.disabledDays){
38824             var day = value.getDay();
38825             for(var i = 0; i < this.disabledDays.length; i++) {
38826                 if(day === this.disabledDays[i]){
38827                     this.markInvalid(this.disabledDaysText);
38828                     return false;
38829                 }
38830             }
38831         }
38832         */
38833         var fvalue = this.formatDate(value);
38834         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38835             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38836             return false;
38837         }
38838         */
38839         return true;
38840     },
38841
38842     // private
38843     // Provides logic to override the default TriggerField.validateBlur which just returns true
38844     validateBlur : function(){
38845         return !this.menu || !this.menu.isVisible();
38846     },
38847
38848     /**
38849      * Returns the current date value of the date field.
38850      * @return {Date} The date value
38851      */
38852     getValue : function(){
38853         
38854         
38855         
38856         return  this.hiddenField ?
38857                 this.hiddenField.value :
38858                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38859     },
38860
38861     /**
38862      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38863      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38864      * (the default format used is "m/d/y").
38865      * <br />Usage:
38866      * <pre><code>
38867 //All of these calls set the same date value (May 4, 2006)
38868
38869 //Pass a date object:
38870 var dt = new Date('5/4/06');
38871 monthField.setValue(dt);
38872
38873 //Pass a date string (default format):
38874 monthField.setValue('5/4/06');
38875
38876 //Pass a date string (custom format):
38877 monthField.format = 'Y-m-d';
38878 monthField.setValue('2006-5-4');
38879 </code></pre>
38880      * @param {String/Date} date The date or valid date string
38881      */
38882     setValue : function(date){
38883         Roo.log('month setValue' + date);
38884         // can only be first of month..
38885         
38886         var val = this.parseDate(date);
38887         
38888         if (this.hiddenField) {
38889             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38890         }
38891         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38892         this.value = this.parseDate(date);
38893     },
38894
38895     // private
38896     parseDate : function(value){
38897         if(!value || value instanceof Date){
38898             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38899             return value;
38900         }
38901         var v = Date.parseDate(value, this.format);
38902         if (!v && this.useIso) {
38903             v = Date.parseDate(value, 'Y-m-d');
38904         }
38905         if (v) {
38906             // 
38907             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38908         }
38909         
38910         
38911         if(!v && this.altFormats){
38912             if(!this.altFormatsArray){
38913                 this.altFormatsArray = this.altFormats.split("|");
38914             }
38915             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38916                 v = Date.parseDate(value, this.altFormatsArray[i]);
38917             }
38918         }
38919         return v;
38920     },
38921
38922     // private
38923     formatDate : function(date, fmt){
38924         return (!date || !(date instanceof Date)) ?
38925                date : date.dateFormat(fmt || this.format);
38926     },
38927
38928     // private
38929     menuListeners : {
38930         select: function(m, d){
38931             this.setValue(d);
38932             this.fireEvent('select', this, d);
38933         },
38934         show : function(){ // retain focus styling
38935             this.onFocus();
38936         },
38937         hide : function(){
38938             this.focus.defer(10, this);
38939             var ml = this.menuListeners;
38940             this.menu.un("select", ml.select,  this);
38941             this.menu.un("show", ml.show,  this);
38942             this.menu.un("hide", ml.hide,  this);
38943         }
38944     },
38945     // private
38946     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38947     onTriggerClick : function(){
38948         if(this.disabled){
38949             return;
38950         }
38951         if(this.menu == null){
38952             this.menu = new Roo.menu.DateMenu();
38953            
38954         }
38955         
38956         Roo.apply(this.menu.picker,  {
38957             
38958             showClear: this.allowBlank,
38959             minDate : this.minValue,
38960             maxDate : this.maxValue,
38961             disabledDatesRE : this.ddMatch,
38962             disabledDatesText : this.disabledDatesText,
38963             
38964             format : this.useIso ? 'Y-m-d' : this.format,
38965             minText : String.format(this.minText, this.formatDate(this.minValue)),
38966             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38967             
38968         });
38969          this.menu.on(Roo.apply({}, this.menuListeners, {
38970             scope:this
38971         }));
38972        
38973         
38974         var m = this.menu;
38975         var p = m.picker;
38976         
38977         // hide month picker get's called when we called by 'before hide';
38978         
38979         var ignorehide = true;
38980         p.hideMonthPicker  = function(disableAnim){
38981             if (ignorehide) {
38982                 return;
38983             }
38984              if(this.monthPicker){
38985                 Roo.log("hideMonthPicker called");
38986                 if(disableAnim === true){
38987                     this.monthPicker.hide();
38988                 }else{
38989                     this.monthPicker.slideOut('t', {duration:.2});
38990                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38991                     p.fireEvent("select", this, this.value);
38992                     m.hide();
38993                 }
38994             }
38995         }
38996         
38997         Roo.log('picker set value');
38998         Roo.log(this.getValue());
38999         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39000         m.show(this.el, 'tl-bl?');
39001         ignorehide  = false;
39002         // this will trigger hideMonthPicker..
39003         
39004         
39005         // hidden the day picker
39006         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39007         
39008         
39009         
39010       
39011         
39012         p.showMonthPicker.defer(100, p);
39013     
39014         
39015        
39016     },
39017
39018     beforeBlur : function(){
39019         var v = this.parseDate(this.getRawValue());
39020         if(v){
39021             this.setValue(v);
39022         }
39023     }
39024
39025     /** @cfg {Boolean} grow @hide */
39026     /** @cfg {Number} growMin @hide */
39027     /** @cfg {Number} growMax @hide */
39028     /**
39029      * @hide
39030      * @method autoSize
39031      */
39032 });/*
39033  * Based on:
39034  * Ext JS Library 1.1.1
39035  * Copyright(c) 2006-2007, Ext JS, LLC.
39036  *
39037  * Originally Released Under LGPL - original licence link has changed is not relivant.
39038  *
39039  * Fork - LGPL
39040  * <script type="text/javascript">
39041  */
39042  
39043
39044 /**
39045  * @class Roo.form.ComboBox
39046  * @extends Roo.form.TriggerField
39047  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39048  * @constructor
39049  * Create a new ComboBox.
39050  * @param {Object} config Configuration options
39051  */
39052 Roo.form.ComboBox = function(config){
39053     Roo.form.ComboBox.superclass.constructor.call(this, config);
39054     this.addEvents({
39055         /**
39056          * @event expand
39057          * Fires when the dropdown list is expanded
39058              * @param {Roo.form.ComboBox} combo This combo box
39059              */
39060         'expand' : true,
39061         /**
39062          * @event collapse
39063          * Fires when the dropdown list is collapsed
39064              * @param {Roo.form.ComboBox} combo This combo box
39065              */
39066         'collapse' : true,
39067         /**
39068          * @event beforeselect
39069          * Fires before a list item is selected. Return false to cancel the selection.
39070              * @param {Roo.form.ComboBox} combo This combo box
39071              * @param {Roo.data.Record} record The data record returned from the underlying store
39072              * @param {Number} index The index of the selected item in the dropdown list
39073              */
39074         'beforeselect' : true,
39075         /**
39076          * @event select
39077          * Fires when a list item is selected
39078              * @param {Roo.form.ComboBox} combo This combo box
39079              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39080              * @param {Number} index The index of the selected item in the dropdown list
39081              */
39082         'select' : true,
39083         /**
39084          * @event beforequery
39085          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39086          * The event object passed has these properties:
39087              * @param {Roo.form.ComboBox} combo This combo box
39088              * @param {String} query The query
39089              * @param {Boolean} forceAll true to force "all" query
39090              * @param {Boolean} cancel true to cancel the query
39091              * @param {Object} e The query event object
39092              */
39093         'beforequery': true,
39094          /**
39095          * @event add
39096          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39097              * @param {Roo.form.ComboBox} combo This combo box
39098              */
39099         'add' : true,
39100         /**
39101          * @event edit
39102          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39103              * @param {Roo.form.ComboBox} combo This combo box
39104              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39105              */
39106         'edit' : true
39107         
39108         
39109     });
39110     if(this.transform){
39111         this.allowDomMove = false;
39112         var s = Roo.getDom(this.transform);
39113         if(!this.hiddenName){
39114             this.hiddenName = s.name;
39115         }
39116         if(!this.store){
39117             this.mode = 'local';
39118             var d = [], opts = s.options;
39119             for(var i = 0, len = opts.length;i < len; i++){
39120                 var o = opts[i];
39121                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39122                 if(o.selected) {
39123                     this.value = value;
39124                 }
39125                 d.push([value, o.text]);
39126             }
39127             this.store = new Roo.data.SimpleStore({
39128                 'id': 0,
39129                 fields: ['value', 'text'],
39130                 data : d
39131             });
39132             this.valueField = 'value';
39133             this.displayField = 'text';
39134         }
39135         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39136         if(!this.lazyRender){
39137             this.target = true;
39138             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39139             s.parentNode.removeChild(s); // remove it
39140             this.render(this.el.parentNode);
39141         }else{
39142             s.parentNode.removeChild(s); // remove it
39143         }
39144
39145     }
39146     if (this.store) {
39147         this.store = Roo.factory(this.store, Roo.data);
39148     }
39149     
39150     this.selectedIndex = -1;
39151     if(this.mode == 'local'){
39152         if(config.queryDelay === undefined){
39153             this.queryDelay = 10;
39154         }
39155         if(config.minChars === undefined){
39156             this.minChars = 0;
39157         }
39158     }
39159 };
39160
39161 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39162     /**
39163      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39164      */
39165     /**
39166      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39167      * rendering into an Roo.Editor, defaults to false)
39168      */
39169     /**
39170      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39171      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39172      */
39173     /**
39174      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39175      */
39176     /**
39177      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39178      * the dropdown list (defaults to undefined, with no header element)
39179      */
39180
39181      /**
39182      * @cfg {String/Roo.Template} tpl The template to use to render the output
39183      */
39184      
39185     // private
39186     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39187     /**
39188      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39189      */
39190     listWidth: undefined,
39191     /**
39192      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39193      * mode = 'remote' or 'text' if mode = 'local')
39194      */
39195     displayField: undefined,
39196     /**
39197      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39198      * mode = 'remote' or 'value' if mode = 'local'). 
39199      * Note: use of a valueField requires the user make a selection
39200      * in order for a value to be mapped.
39201      */
39202     valueField: undefined,
39203     
39204     
39205     /**
39206      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39207      * field's data value (defaults to the underlying DOM element's name)
39208      */
39209     hiddenName: undefined,
39210     /**
39211      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39212      */
39213     listClass: '',
39214     /**
39215      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39216      */
39217     selectedClass: 'x-combo-selected',
39218     /**
39219      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39220      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39221      * which displays a downward arrow icon).
39222      */
39223     triggerClass : 'x-form-arrow-trigger',
39224     /**
39225      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39226      */
39227     shadow:'sides',
39228     /**
39229      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39230      * anchor positions (defaults to 'tl-bl')
39231      */
39232     listAlign: 'tl-bl?',
39233     /**
39234      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39235      */
39236     maxHeight: 300,
39237     /**
39238      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39239      * query specified by the allQuery config option (defaults to 'query')
39240      */
39241     triggerAction: 'query',
39242     /**
39243      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39244      * (defaults to 4, does not apply if editable = false)
39245      */
39246     minChars : 4,
39247     /**
39248      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39249      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39250      */
39251     typeAhead: false,
39252     /**
39253      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39254      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39255      */
39256     queryDelay: 500,
39257     /**
39258      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39259      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39260      */
39261     pageSize: 0,
39262     /**
39263      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39264      * when editable = true (defaults to false)
39265      */
39266     selectOnFocus:false,
39267     /**
39268      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39269      */
39270     queryParam: 'query',
39271     /**
39272      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39273      * when mode = 'remote' (defaults to 'Loading...')
39274      */
39275     loadingText: 'Loading...',
39276     /**
39277      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39278      */
39279     resizable: false,
39280     /**
39281      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39282      */
39283     handleHeight : 8,
39284     /**
39285      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39286      * traditional select (defaults to true)
39287      */
39288     editable: true,
39289     /**
39290      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39291      */
39292     allQuery: '',
39293     /**
39294      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39295      */
39296     mode: 'remote',
39297     /**
39298      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39299      * listWidth has a higher value)
39300      */
39301     minListWidth : 70,
39302     /**
39303      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39304      * allow the user to set arbitrary text into the field (defaults to false)
39305      */
39306     forceSelection:false,
39307     /**
39308      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39309      * if typeAhead = true (defaults to 250)
39310      */
39311     typeAheadDelay : 250,
39312     /**
39313      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39314      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39315      */
39316     valueNotFoundText : undefined,
39317     /**
39318      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39319      */
39320     blockFocus : false,
39321     
39322     /**
39323      * @cfg {Boolean} disableClear Disable showing of clear button.
39324      */
39325     disableClear : false,
39326     /**
39327      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39328      */
39329     alwaysQuery : false,
39330     
39331     //private
39332     addicon : false,
39333     editicon: false,
39334     
39335     // element that contains real text value.. (when hidden is used..)
39336      
39337     // private
39338     onRender : function(ct, position){
39339         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39340         if(this.hiddenName){
39341             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39342                     'before', true);
39343             this.hiddenField.value =
39344                 this.hiddenValue !== undefined ? this.hiddenValue :
39345                 this.value !== undefined ? this.value : '';
39346
39347             // prevent input submission
39348             this.el.dom.removeAttribute('name');
39349              
39350              
39351         }
39352         if(Roo.isGecko){
39353             this.el.dom.setAttribute('autocomplete', 'off');
39354         }
39355
39356         var cls = 'x-combo-list';
39357
39358         this.list = new Roo.Layer({
39359             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39360         });
39361
39362         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39363         this.list.setWidth(lw);
39364         this.list.swallowEvent('mousewheel');
39365         this.assetHeight = 0;
39366
39367         if(this.title){
39368             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39369             this.assetHeight += this.header.getHeight();
39370         }
39371
39372         this.innerList = this.list.createChild({cls:cls+'-inner'});
39373         this.innerList.on('mouseover', this.onViewOver, this);
39374         this.innerList.on('mousemove', this.onViewMove, this);
39375         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39376         
39377         if(this.allowBlank && !this.pageSize && !this.disableClear){
39378             this.footer = this.list.createChild({cls:cls+'-ft'});
39379             this.pageTb = new Roo.Toolbar(this.footer);
39380            
39381         }
39382         if(this.pageSize){
39383             this.footer = this.list.createChild({cls:cls+'-ft'});
39384             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39385                     {pageSize: this.pageSize});
39386             
39387         }
39388         
39389         if (this.pageTb && this.allowBlank && !this.disableClear) {
39390             var _this = this;
39391             this.pageTb.add(new Roo.Toolbar.Fill(), {
39392                 cls: 'x-btn-icon x-btn-clear',
39393                 text: '&#160;',
39394                 handler: function()
39395                 {
39396                     _this.collapse();
39397                     _this.clearValue();
39398                     _this.onSelect(false, -1);
39399                 }
39400             });
39401         }
39402         if (this.footer) {
39403             this.assetHeight += this.footer.getHeight();
39404         }
39405         
39406
39407         if(!this.tpl){
39408             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39409         }
39410
39411         this.view = new Roo.View(this.innerList, this.tpl, {
39412             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39413         });
39414
39415         this.view.on('click', this.onViewClick, this);
39416
39417         this.store.on('beforeload', this.onBeforeLoad, this);
39418         this.store.on('load', this.onLoad, this);
39419         this.store.on('loadexception', this.onLoadException, this);
39420
39421         if(this.resizable){
39422             this.resizer = new Roo.Resizable(this.list,  {
39423                pinned:true, handles:'se'
39424             });
39425             this.resizer.on('resize', function(r, w, h){
39426                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39427                 this.listWidth = w;
39428                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39429                 this.restrictHeight();
39430             }, this);
39431             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39432         }
39433         if(!this.editable){
39434             this.editable = true;
39435             this.setEditable(false);
39436         }  
39437         
39438         
39439         if (typeof(this.events.add.listeners) != 'undefined') {
39440             
39441             this.addicon = this.wrap.createChild(
39442                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39443        
39444             this.addicon.on('click', function(e) {
39445                 this.fireEvent('add', this);
39446             }, this);
39447         }
39448         if (typeof(this.events.edit.listeners) != 'undefined') {
39449             
39450             this.editicon = this.wrap.createChild(
39451                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39452             if (this.addicon) {
39453                 this.editicon.setStyle('margin-left', '40px');
39454             }
39455             this.editicon.on('click', function(e) {
39456                 
39457                 // we fire even  if inothing is selected..
39458                 this.fireEvent('edit', this, this.lastData );
39459                 
39460             }, this);
39461         }
39462         
39463         
39464         
39465     },
39466
39467     // private
39468     initEvents : function(){
39469         Roo.form.ComboBox.superclass.initEvents.call(this);
39470
39471         this.keyNav = new Roo.KeyNav(this.el, {
39472             "up" : function(e){
39473                 this.inKeyMode = true;
39474                 this.selectPrev();
39475             },
39476
39477             "down" : function(e){
39478                 if(!this.isExpanded()){
39479                     this.onTriggerClick();
39480                 }else{
39481                     this.inKeyMode = true;
39482                     this.selectNext();
39483                 }
39484             },
39485
39486             "enter" : function(e){
39487                 this.onViewClick();
39488                 //return true;
39489             },
39490
39491             "esc" : function(e){
39492                 this.collapse();
39493             },
39494
39495             "tab" : function(e){
39496                 this.onViewClick(false);
39497                 this.fireEvent("specialkey", this, e);
39498                 return true;
39499             },
39500
39501             scope : this,
39502
39503             doRelay : function(foo, bar, hname){
39504                 if(hname == 'down' || this.scope.isExpanded()){
39505                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39506                 }
39507                 return true;
39508             },
39509
39510             forceKeyDown: true
39511         });
39512         this.queryDelay = Math.max(this.queryDelay || 10,
39513                 this.mode == 'local' ? 10 : 250);
39514         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39515         if(this.typeAhead){
39516             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39517         }
39518         if(this.editable !== false){
39519             this.el.on("keyup", this.onKeyUp, this);
39520         }
39521         if(this.forceSelection){
39522             this.on('blur', this.doForce, this);
39523         }
39524     },
39525
39526     onDestroy : function(){
39527         if(this.view){
39528             this.view.setStore(null);
39529             this.view.el.removeAllListeners();
39530             this.view.el.remove();
39531             this.view.purgeListeners();
39532         }
39533         if(this.list){
39534             this.list.destroy();
39535         }
39536         if(this.store){
39537             this.store.un('beforeload', this.onBeforeLoad, this);
39538             this.store.un('load', this.onLoad, this);
39539             this.store.un('loadexception', this.onLoadException, this);
39540         }
39541         Roo.form.ComboBox.superclass.onDestroy.call(this);
39542     },
39543
39544     // private
39545     fireKey : function(e){
39546         if(e.isNavKeyPress() && !this.list.isVisible()){
39547             this.fireEvent("specialkey", this, e);
39548         }
39549     },
39550
39551     // private
39552     onResize: function(w, h){
39553         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39554         
39555         if(typeof w != 'number'){
39556             // we do not handle it!?!?
39557             return;
39558         }
39559         var tw = this.trigger.getWidth();
39560         tw += this.addicon ? this.addicon.getWidth() : 0;
39561         tw += this.editicon ? this.editicon.getWidth() : 0;
39562         var x = w - tw;
39563         this.el.setWidth( this.adjustWidth('input', x));
39564             
39565         this.trigger.setStyle('left', x+'px');
39566         
39567         if(this.list && this.listWidth === undefined){
39568             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39569             this.list.setWidth(lw);
39570             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39571         }
39572         
39573     
39574         
39575     },
39576
39577     /**
39578      * Allow or prevent the user from directly editing the field text.  If false is passed,
39579      * the user will only be able to select from the items defined in the dropdown list.  This method
39580      * is the runtime equivalent of setting the 'editable' config option at config time.
39581      * @param {Boolean} value True to allow the user to directly edit the field text
39582      */
39583     setEditable : function(value){
39584         if(value == this.editable){
39585             return;
39586         }
39587         this.editable = value;
39588         if(!value){
39589             this.el.dom.setAttribute('readOnly', true);
39590             this.el.on('mousedown', this.onTriggerClick,  this);
39591             this.el.addClass('x-combo-noedit');
39592         }else{
39593             this.el.dom.setAttribute('readOnly', false);
39594             this.el.un('mousedown', this.onTriggerClick,  this);
39595             this.el.removeClass('x-combo-noedit');
39596         }
39597     },
39598
39599     // private
39600     onBeforeLoad : function(){
39601         if(!this.hasFocus){
39602             return;
39603         }
39604         this.innerList.update(this.loadingText ?
39605                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39606         this.restrictHeight();
39607         this.selectedIndex = -1;
39608     },
39609
39610     // private
39611     onLoad : function(){
39612         if(!this.hasFocus){
39613             return;
39614         }
39615         if(this.store.getCount() > 0){
39616             this.expand();
39617             this.restrictHeight();
39618             if(this.lastQuery == this.allQuery){
39619                 if(this.editable){
39620                     this.el.dom.select();
39621                 }
39622                 if(!this.selectByValue(this.value, true)){
39623                     this.select(0, true);
39624                 }
39625             }else{
39626                 this.selectNext();
39627                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39628                     this.taTask.delay(this.typeAheadDelay);
39629                 }
39630             }
39631         }else{
39632             this.onEmptyResults();
39633         }
39634         //this.el.focus();
39635     },
39636     // private
39637     onLoadException : function()
39638     {
39639         this.collapse();
39640         Roo.log(this.store.reader.jsonData);
39641         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39642             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39643         }
39644         
39645         
39646     },
39647     // private
39648     onTypeAhead : function(){
39649         if(this.store.getCount() > 0){
39650             var r = this.store.getAt(0);
39651             var newValue = r.data[this.displayField];
39652             var len = newValue.length;
39653             var selStart = this.getRawValue().length;
39654             if(selStart != len){
39655                 this.setRawValue(newValue);
39656                 this.selectText(selStart, newValue.length);
39657             }
39658         }
39659     },
39660
39661     // private
39662     onSelect : function(record, index){
39663         if(this.fireEvent('beforeselect', this, record, index) !== false){
39664             this.setFromData(index > -1 ? record.data : false);
39665             this.collapse();
39666             this.fireEvent('select', this, record, index);
39667         }
39668     },
39669
39670     /**
39671      * Returns the currently selected field value or empty string if no value is set.
39672      * @return {String} value The selected value
39673      */
39674     getValue : function(){
39675         if(this.valueField){
39676             return typeof this.value != 'undefined' ? this.value : '';
39677         }else{
39678             return Roo.form.ComboBox.superclass.getValue.call(this);
39679         }
39680     },
39681
39682     /**
39683      * Clears any text/value currently set in the field
39684      */
39685     clearValue : function(){
39686         if(this.hiddenField){
39687             this.hiddenField.value = '';
39688         }
39689         this.value = '';
39690         this.setRawValue('');
39691         this.lastSelectionText = '';
39692         
39693     },
39694
39695     /**
39696      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39697      * will be displayed in the field.  If the value does not match the data value of an existing item,
39698      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39699      * Otherwise the field will be blank (although the value will still be set).
39700      * @param {String} value The value to match
39701      */
39702     setValue : function(v){
39703         var text = v;
39704         if(this.valueField){
39705             var r = this.findRecord(this.valueField, v);
39706             if(r){
39707                 text = r.data[this.displayField];
39708             }else if(this.valueNotFoundText !== undefined){
39709                 text = this.valueNotFoundText;
39710             }
39711         }
39712         this.lastSelectionText = text;
39713         if(this.hiddenField){
39714             this.hiddenField.value = v;
39715         }
39716         Roo.form.ComboBox.superclass.setValue.call(this, text);
39717         this.value = v;
39718     },
39719     /**
39720      * @property {Object} the last set data for the element
39721      */
39722     
39723     lastData : false,
39724     /**
39725      * Sets the value of the field based on a object which is related to the record format for the store.
39726      * @param {Object} value the value to set as. or false on reset?
39727      */
39728     setFromData : function(o){
39729         var dv = ''; // display value
39730         var vv = ''; // value value..
39731         this.lastData = o;
39732         if (this.displayField) {
39733             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39734         } else {
39735             // this is an error condition!!!
39736             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39737         }
39738         
39739         if(this.valueField){
39740             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39741         }
39742         if(this.hiddenField){
39743             this.hiddenField.value = vv;
39744             
39745             this.lastSelectionText = dv;
39746             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39747             this.value = vv;
39748             return;
39749         }
39750         // no hidden field.. - we store the value in 'value', but still display
39751         // display field!!!!
39752         this.lastSelectionText = dv;
39753         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39754         this.value = vv;
39755         
39756         
39757     },
39758     // private
39759     reset : function(){
39760         // overridden so that last data is reset..
39761         this.setValue(this.resetValue);
39762         this.clearInvalid();
39763         this.lastData = false;
39764         if (this.view) {
39765             this.view.clearSelections();
39766         }
39767     },
39768     // private
39769     findRecord : function(prop, value){
39770         var record;
39771         if(this.store.getCount() > 0){
39772             this.store.each(function(r){
39773                 if(r.data[prop] == value){
39774                     record = r;
39775                     return false;
39776                 }
39777                 return true;
39778             });
39779         }
39780         return record;
39781     },
39782     
39783     getName: function()
39784     {
39785         // returns hidden if it's set..
39786         if (!this.rendered) {return ''};
39787         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39788         
39789     },
39790     // private
39791     onViewMove : function(e, t){
39792         this.inKeyMode = false;
39793     },
39794
39795     // private
39796     onViewOver : function(e, t){
39797         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39798             return;
39799         }
39800         var item = this.view.findItemFromChild(t);
39801         if(item){
39802             var index = this.view.indexOf(item);
39803             this.select(index, false);
39804         }
39805     },
39806
39807     // private
39808     onViewClick : function(doFocus)
39809     {
39810         var index = this.view.getSelectedIndexes()[0];
39811         var r = this.store.getAt(index);
39812         if(r){
39813             this.onSelect(r, index);
39814         }
39815         if(doFocus !== false && !this.blockFocus){
39816             this.el.focus();
39817         }
39818     },
39819
39820     // private
39821     restrictHeight : function(){
39822         this.innerList.dom.style.height = '';
39823         var inner = this.innerList.dom;
39824         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39825         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39826         this.list.beginUpdate();
39827         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39828         this.list.alignTo(this.el, this.listAlign);
39829         this.list.endUpdate();
39830     },
39831
39832     // private
39833     onEmptyResults : function(){
39834         this.collapse();
39835     },
39836
39837     /**
39838      * Returns true if the dropdown list is expanded, else false.
39839      */
39840     isExpanded : function(){
39841         return this.list.isVisible();
39842     },
39843
39844     /**
39845      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39846      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39847      * @param {String} value The data value of the item to select
39848      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39849      * selected item if it is not currently in view (defaults to true)
39850      * @return {Boolean} True if the value matched an item in the list, else false
39851      */
39852     selectByValue : function(v, scrollIntoView){
39853         if(v !== undefined && v !== null){
39854             var r = this.findRecord(this.valueField || this.displayField, v);
39855             if(r){
39856                 this.select(this.store.indexOf(r), scrollIntoView);
39857                 return true;
39858             }
39859         }
39860         return false;
39861     },
39862
39863     /**
39864      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39865      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39866      * @param {Number} index The zero-based index of the list item to select
39867      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39868      * selected item if it is not currently in view (defaults to true)
39869      */
39870     select : function(index, scrollIntoView){
39871         this.selectedIndex = index;
39872         this.view.select(index);
39873         if(scrollIntoView !== false){
39874             var el = this.view.getNode(index);
39875             if(el){
39876                 this.innerList.scrollChildIntoView(el, false);
39877             }
39878         }
39879     },
39880
39881     // private
39882     selectNext : function(){
39883         var ct = this.store.getCount();
39884         if(ct > 0){
39885             if(this.selectedIndex == -1){
39886                 this.select(0);
39887             }else if(this.selectedIndex < ct-1){
39888                 this.select(this.selectedIndex+1);
39889             }
39890         }
39891     },
39892
39893     // private
39894     selectPrev : function(){
39895         var ct = this.store.getCount();
39896         if(ct > 0){
39897             if(this.selectedIndex == -1){
39898                 this.select(0);
39899             }else if(this.selectedIndex != 0){
39900                 this.select(this.selectedIndex-1);
39901             }
39902         }
39903     },
39904
39905     // private
39906     onKeyUp : function(e){
39907         if(this.editable !== false && !e.isSpecialKey()){
39908             this.lastKey = e.getKey();
39909             this.dqTask.delay(this.queryDelay);
39910         }
39911     },
39912
39913     // private
39914     validateBlur : function(){
39915         return !this.list || !this.list.isVisible();   
39916     },
39917
39918     // private
39919     initQuery : function(){
39920         this.doQuery(this.getRawValue());
39921     },
39922
39923     // private
39924     doForce : function(){
39925         if(this.el.dom.value.length > 0){
39926             this.el.dom.value =
39927                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39928              
39929         }
39930     },
39931
39932     /**
39933      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39934      * query allowing the query action to be canceled if needed.
39935      * @param {String} query The SQL query to execute
39936      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39937      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39938      * saved in the current store (defaults to false)
39939      */
39940     doQuery : function(q, forceAll){
39941         if(q === undefined || q === null){
39942             q = '';
39943         }
39944         var qe = {
39945             query: q,
39946             forceAll: forceAll,
39947             combo: this,
39948             cancel:false
39949         };
39950         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39951             return false;
39952         }
39953         q = qe.query;
39954         forceAll = qe.forceAll;
39955         if(forceAll === true || (q.length >= this.minChars)){
39956             if(this.lastQuery != q || this.alwaysQuery){
39957                 this.lastQuery = q;
39958                 if(this.mode == 'local'){
39959                     this.selectedIndex = -1;
39960                     if(forceAll){
39961                         this.store.clearFilter();
39962                     }else{
39963                         this.store.filter(this.displayField, q);
39964                     }
39965                     this.onLoad();
39966                 }else{
39967                     this.store.baseParams[this.queryParam] = q;
39968                     this.store.load({
39969                         params: this.getParams(q)
39970                     });
39971                     this.expand();
39972                 }
39973             }else{
39974                 this.selectedIndex = -1;
39975                 this.onLoad();   
39976             }
39977         }
39978     },
39979
39980     // private
39981     getParams : function(q){
39982         var p = {};
39983         //p[this.queryParam] = q;
39984         if(this.pageSize){
39985             p.start = 0;
39986             p.limit = this.pageSize;
39987         }
39988         return p;
39989     },
39990
39991     /**
39992      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39993      */
39994     collapse : function(){
39995         if(!this.isExpanded()){
39996             return;
39997         }
39998         this.list.hide();
39999         Roo.get(document).un('mousedown', this.collapseIf, this);
40000         Roo.get(document).un('mousewheel', this.collapseIf, this);
40001         if (!this.editable) {
40002             Roo.get(document).un('keydown', this.listKeyPress, this);
40003         }
40004         this.fireEvent('collapse', this);
40005     },
40006
40007     // private
40008     collapseIf : function(e){
40009         if(!e.within(this.wrap) && !e.within(this.list)){
40010             this.collapse();
40011         }
40012     },
40013
40014     /**
40015      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40016      */
40017     expand : function(){
40018         if(this.isExpanded() || !this.hasFocus){
40019             return;
40020         }
40021         this.list.alignTo(this.el, this.listAlign);
40022         this.list.show();
40023         Roo.get(document).on('mousedown', this.collapseIf, this);
40024         Roo.get(document).on('mousewheel', this.collapseIf, this);
40025         if (!this.editable) {
40026             Roo.get(document).on('keydown', this.listKeyPress, this);
40027         }
40028         
40029         this.fireEvent('expand', this);
40030     },
40031
40032     // private
40033     // Implements the default empty TriggerField.onTriggerClick function
40034     onTriggerClick : function(){
40035         if(this.disabled){
40036             return;
40037         }
40038         if(this.isExpanded()){
40039             this.collapse();
40040             if (!this.blockFocus) {
40041                 this.el.focus();
40042             }
40043             
40044         }else {
40045             this.hasFocus = true;
40046             if(this.triggerAction == 'all') {
40047                 this.doQuery(this.allQuery, true);
40048             } else {
40049                 this.doQuery(this.getRawValue());
40050             }
40051             if (!this.blockFocus) {
40052                 this.el.focus();
40053             }
40054         }
40055     },
40056     listKeyPress : function(e)
40057     {
40058         //Roo.log('listkeypress');
40059         // scroll to first matching element based on key pres..
40060         if (e.isSpecialKey()) {
40061             return false;
40062         }
40063         var k = String.fromCharCode(e.getKey()).toUpperCase();
40064         //Roo.log(k);
40065         var match  = false;
40066         var csel = this.view.getSelectedNodes();
40067         var cselitem = false;
40068         if (csel.length) {
40069             var ix = this.view.indexOf(csel[0]);
40070             cselitem  = this.store.getAt(ix);
40071             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40072                 cselitem = false;
40073             }
40074             
40075         }
40076         
40077         this.store.each(function(v) { 
40078             if (cselitem) {
40079                 // start at existing selection.
40080                 if (cselitem.id == v.id) {
40081                     cselitem = false;
40082                 }
40083                 return;
40084             }
40085                 
40086             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40087                 match = this.store.indexOf(v);
40088                 return false;
40089             }
40090         }, this);
40091         
40092         if (match === false) {
40093             return true; // no more action?
40094         }
40095         // scroll to?
40096         this.view.select(match);
40097         var sn = Roo.get(this.view.getSelectedNodes()[0])
40098         sn.scrollIntoView(sn.dom.parentNode, false);
40099     }
40100
40101     /** 
40102     * @cfg {Boolean} grow 
40103     * @hide 
40104     */
40105     /** 
40106     * @cfg {Number} growMin 
40107     * @hide 
40108     */
40109     /** 
40110     * @cfg {Number} growMax 
40111     * @hide 
40112     */
40113     /**
40114      * @hide
40115      * @method autoSize
40116      */
40117 });/*
40118  * Copyright(c) 2010-2012, Roo J Solutions Limited
40119  *
40120  * Licence LGPL
40121  *
40122  */
40123
40124 /**
40125  * @class Roo.form.ComboBoxArray
40126  * @extends Roo.form.TextField
40127  * A facebook style adder... for lists of email / people / countries  etc...
40128  * pick multiple items from a combo box, and shows each one.
40129  *
40130  *  Fred [x]  Brian [x]  [Pick another |v]
40131  *
40132  *
40133  *  For this to work: it needs various extra information
40134  *    - normal combo problay has
40135  *      name, hiddenName
40136  *    + displayField, valueField
40137  *
40138  *    For our purpose...
40139  *
40140  *
40141  *   If we change from 'extends' to wrapping...
40142  *   
40143  *  
40144  *
40145  
40146  
40147  * @constructor
40148  * Create a new ComboBoxArray.
40149  * @param {Object} config Configuration options
40150  */
40151  
40152
40153 Roo.form.ComboBoxArray = function(config)
40154 {
40155     
40156     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40157     
40158     this.items = new Roo.util.MixedCollection(false);
40159     
40160     // construct the child combo...
40161     
40162     
40163     
40164     
40165    
40166     
40167 }
40168
40169  
40170 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40171
40172     /**
40173      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40174      */
40175     
40176     lastData : false,
40177     
40178     // behavies liek a hiddne field
40179     inputType:      'hidden',
40180     /**
40181      * @cfg {Number} width The width of the box that displays the selected element
40182      */ 
40183     width:          300,
40184
40185     
40186     
40187     /**
40188      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40189      */
40190     name : false,
40191     /**
40192      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40193      */
40194     hiddenName : false,
40195     
40196     
40197     // private the array of items that are displayed..
40198     items  : false,
40199     // private - the hidden field el.
40200     hiddenEl : false,
40201     // private - the filed el..
40202     el : false,
40203     
40204     //validateValue : function() { return true; }, // all values are ok!
40205     //onAddClick: function() { },
40206     
40207     onRender : function(ct, position) 
40208     {
40209         
40210         // create the standard hidden element
40211         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40212         
40213         
40214         // give fake names to child combo;
40215         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40216         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40217         
40218         this.combo = Roo.factory(this.combo, Roo.form);
40219         this.combo.onRender(ct, position);
40220         if (typeof(this.combo.width) != 'undefined') {
40221             this.combo.onResize(this.combo.width,0);
40222         }
40223         
40224         this.combo.initEvents();
40225         
40226         // assigned so form know we need to do this..
40227         this.store          = this.combo.store;
40228         this.valueField     = this.combo.valueField;
40229         this.displayField   = this.combo.displayField ;
40230         
40231         
40232         this.combo.wrap.addClass('x-cbarray-grp');
40233         
40234         var cbwrap = this.combo.wrap.createChild(
40235             {tag: 'div', cls: 'x-cbarray-cb'},
40236             this.combo.el.dom
40237         );
40238         
40239              
40240         this.hiddenEl = this.combo.wrap.createChild({
40241             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40242         });
40243         this.el = this.combo.wrap.createChild({
40244             tag: 'input',  type:'hidden' , name: this.name, value : ''
40245         });
40246          //   this.el.dom.removeAttribute("name");
40247         
40248         
40249         this.outerWrap = this.combo.wrap;
40250         this.wrap = cbwrap;
40251         
40252         this.outerWrap.setWidth(this.width);
40253         this.outerWrap.dom.removeChild(this.el.dom);
40254         
40255         this.wrap.dom.appendChild(this.el.dom);
40256         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40257         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40258         
40259         this.combo.trigger.setStyle('position','relative');
40260         this.combo.trigger.setStyle('left', '0px');
40261         this.combo.trigger.setStyle('top', '2px');
40262         
40263         this.combo.el.setStyle('vertical-align', 'text-bottom');
40264         
40265         //this.trigger.setStyle('vertical-align', 'top');
40266         
40267         // this should use the code from combo really... on('add' ....)
40268         if (this.adder) {
40269             
40270         
40271             this.adder = this.outerWrap.createChild(
40272                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40273             var _t = this;
40274             this.adder.on('click', function(e) {
40275                 _t.fireEvent('adderclick', this, e);
40276             }, _t);
40277         }
40278         //var _t = this;
40279         //this.adder.on('click', this.onAddClick, _t);
40280         
40281         
40282         this.combo.on('select', function(cb, rec, ix) {
40283             this.addItem(rec.data);
40284             
40285             cb.setValue('');
40286             cb.el.dom.value = '';
40287             //cb.lastData = rec.data;
40288             // add to list
40289             
40290         }, this);
40291         
40292         
40293     },
40294     
40295     
40296     getName: function()
40297     {
40298         // returns hidden if it's set..
40299         if (!this.rendered) {return ''};
40300         return  this.hiddenName ? this.hiddenName : this.name;
40301         
40302     },
40303     
40304     
40305     onResize: function(w, h){
40306         
40307         return;
40308         // not sure if this is needed..
40309         //this.combo.onResize(w,h);
40310         
40311         if(typeof w != 'number'){
40312             // we do not handle it!?!?
40313             return;
40314         }
40315         var tw = this.combo.trigger.getWidth();
40316         tw += this.addicon ? this.addicon.getWidth() : 0;
40317         tw += this.editicon ? this.editicon.getWidth() : 0;
40318         var x = w - tw;
40319         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40320             
40321         this.combo.trigger.setStyle('left', '0px');
40322         
40323         if(this.list && this.listWidth === undefined){
40324             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40325             this.list.setWidth(lw);
40326             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40327         }
40328         
40329     
40330         
40331     },
40332     
40333     addItem: function(rec)
40334     {
40335         var valueField = this.combo.valueField;
40336         var displayField = this.combo.displayField;
40337         if (this.items.indexOfKey(rec[valueField]) > -1) {
40338             //console.log("GOT " + rec.data.id);
40339             return;
40340         }
40341         
40342         var x = new Roo.form.ComboBoxArray.Item({
40343             //id : rec[this.idField],
40344             data : rec,
40345             displayField : displayField ,
40346             tipField : displayField ,
40347             cb : this
40348         });
40349         // use the 
40350         this.items.add(rec[valueField],x);
40351         // add it before the element..
40352         this.updateHiddenEl();
40353         x.render(this.outerWrap, this.wrap.dom);
40354         // add the image handler..
40355     },
40356     
40357     updateHiddenEl : function()
40358     {
40359         this.validate();
40360         if (!this.hiddenEl) {
40361             return;
40362         }
40363         var ar = [];
40364         var idField = this.combo.valueField;
40365         
40366         this.items.each(function(f) {
40367             ar.push(f.data[idField]);
40368            
40369         });
40370         this.hiddenEl.dom.value = ar.join(',');
40371         this.validate();
40372     },
40373     
40374     reset : function()
40375     {
40376         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40377         this.items.each(function(f) {
40378            f.remove(); 
40379         });
40380         this.el.dom.value = '';
40381         if (this.hiddenEl) {
40382             this.hiddenEl.dom.value = '';
40383         }
40384         
40385     },
40386     getValue: function()
40387     {
40388         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40389     },
40390     setValue: function(v) // not a valid action - must use addItems..
40391     {
40392          
40393         this.reset();
40394         
40395         
40396         
40397         if (this.store.isLocal && (typeof(v) == 'string')) {
40398             // then we can use the store to find the values..
40399             // comma seperated at present.. this needs to allow JSON based encoding..
40400             this.hiddenEl.value  = v;
40401             var v_ar = [];
40402             Roo.each(v.split(','), function(k) {
40403                 Roo.log("CHECK " + this.valueField + ',' + k);
40404                 var li = this.store.query(this.valueField, k);
40405                 if (!li.length) {
40406                     return;
40407                 }
40408                 var add = {};
40409                 add[this.valueField] = k;
40410                 add[this.displayField] = li.item(0).data[this.displayField];
40411                 
40412                 this.addItem(add);
40413             }, this) 
40414              
40415         }
40416         if (typeof(v) == 'object') {
40417             // then let's assume it's an array of objects..
40418             Roo.each(v, function(l) {
40419                 this.addItem(l);
40420             }, this);
40421              
40422         }
40423         
40424         
40425     },
40426     setFromData: function(v)
40427     {
40428         // this recieves an object, if setValues is called.
40429         this.reset();
40430         this.el.dom.value = v[this.displayField];
40431         this.hiddenEl.dom.value = v[this.valueField];
40432         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40433             return;
40434         }
40435         var kv = v[this.valueField];
40436         var dv = v[this.displayField];
40437         kv = typeof(kv) != 'string' ? '' : kv;
40438         dv = typeof(dv) != 'string' ? '' : dv;
40439         
40440         
40441         var keys = kv.split(',');
40442         var display = dv.split(',');
40443         for (var i = 0 ; i < keys.length; i++) {
40444             
40445             add = {};
40446             add[this.valueField] = keys[i];
40447             add[this.displayField] = display[i];
40448             this.addItem(add);
40449         }
40450       
40451         
40452     },
40453     
40454     /**
40455      * Validates the combox array value
40456      * @return {Boolean} True if the value is valid, else false
40457      */
40458     validate : function(){
40459         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40460             this.clearInvalid();
40461             return true;
40462         }
40463         return false;
40464     },
40465     
40466     validateValue : function(value){
40467         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40468         
40469     },
40470     
40471     /*@
40472      * overide
40473      * 
40474      */
40475     isDirty : function() {
40476         if(this.disabled) {
40477             return false;
40478         }
40479         
40480         try {
40481             var d = Roo.decode(String(this.originalValue));
40482         } catch (e) {
40483             return String(this.getValue()) !== String(this.originalValue);
40484         }
40485         
40486         var originalValue = [];
40487         
40488         for (var i = 0; i < d.length; i++){
40489             originalValue.push(d[i][this.valueField]);
40490         }
40491         
40492         return String(this.getValue()) !== String(originalValue.join(','));
40493         
40494     }
40495     
40496 });
40497
40498
40499
40500 /**
40501  * @class Roo.form.ComboBoxArray.Item
40502  * @extends Roo.BoxComponent
40503  * A selected item in the list
40504  *  Fred [x]  Brian [x]  [Pick another |v]
40505  * 
40506  * @constructor
40507  * Create a new item.
40508  * @param {Object} config Configuration options
40509  */
40510  
40511 Roo.form.ComboBoxArray.Item = function(config) {
40512     config.id = Roo.id();
40513     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40514 }
40515
40516 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40517     data : {},
40518     cb: false,
40519     displayField : false,
40520     tipField : false,
40521     
40522     
40523     defaultAutoCreate : {
40524         tag: 'div',
40525         cls: 'x-cbarray-item',
40526         cn : [ 
40527             { tag: 'div' },
40528             {
40529                 tag: 'img',
40530                 width:16,
40531                 height : 16,
40532                 src : Roo.BLANK_IMAGE_URL ,
40533                 align: 'center'
40534             }
40535         ]
40536         
40537     },
40538     
40539  
40540     onRender : function(ct, position)
40541     {
40542         Roo.form.Field.superclass.onRender.call(this, ct, position);
40543         
40544         if(!this.el){
40545             var cfg = this.getAutoCreate();
40546             this.el = ct.createChild(cfg, position);
40547         }
40548         
40549         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40550         
40551         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40552             this.cb.renderer(this.data) :
40553             String.format('{0}',this.data[this.displayField]);
40554         
40555             
40556         this.el.child('div').dom.setAttribute('qtip',
40557                         String.format('{0}',this.data[this.tipField])
40558         );
40559         
40560         this.el.child('img').on('click', this.remove, this);
40561         
40562     },
40563    
40564     remove : function()
40565     {
40566         
40567         this.cb.items.remove(this);
40568         this.el.child('img').un('click', this.remove, this);
40569         this.el.remove();
40570         this.cb.updateHiddenEl();
40571     }
40572 });/*
40573  * Based on:
40574  * Ext JS Library 1.1.1
40575  * Copyright(c) 2006-2007, Ext JS, LLC.
40576  *
40577  * Originally Released Under LGPL - original licence link has changed is not relivant.
40578  *
40579  * Fork - LGPL
40580  * <script type="text/javascript">
40581  */
40582 /**
40583  * @class Roo.form.Checkbox
40584  * @extends Roo.form.Field
40585  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40586  * @constructor
40587  * Creates a new Checkbox
40588  * @param {Object} config Configuration options
40589  */
40590 Roo.form.Checkbox = function(config){
40591     Roo.form.Checkbox.superclass.constructor.call(this, config);
40592     this.addEvents({
40593         /**
40594          * @event check
40595          * Fires when the checkbox is checked or unchecked.
40596              * @param {Roo.form.Checkbox} this This checkbox
40597              * @param {Boolean} checked The new checked value
40598              */
40599         check : true
40600     });
40601 };
40602
40603 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40604     /**
40605      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40606      */
40607     focusClass : undefined,
40608     /**
40609      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40610      */
40611     fieldClass: "x-form-field",
40612     /**
40613      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40614      */
40615     checked: false,
40616     /**
40617      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40618      * {tag: "input", type: "checkbox", autocomplete: "off"})
40619      */
40620     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40621     /**
40622      * @cfg {String} boxLabel The text that appears beside the checkbox
40623      */
40624     boxLabel : "",
40625     /**
40626      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40627      */  
40628     inputValue : '1',
40629     /**
40630      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40631      */
40632      valueOff: '0', // value when not checked..
40633
40634     actionMode : 'viewEl', 
40635     //
40636     // private
40637     itemCls : 'x-menu-check-item x-form-item',
40638     groupClass : 'x-menu-group-item',
40639     inputType : 'hidden',
40640     
40641     
40642     inSetChecked: false, // check that we are not calling self...
40643     
40644     inputElement: false, // real input element?
40645     basedOn: false, // ????
40646     
40647     isFormField: true, // not sure where this is needed!!!!
40648
40649     onResize : function(){
40650         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40651         if(!this.boxLabel){
40652             this.el.alignTo(this.wrap, 'c-c');
40653         }
40654     },
40655
40656     initEvents : function(){
40657         Roo.form.Checkbox.superclass.initEvents.call(this);
40658         this.el.on("click", this.onClick,  this);
40659         this.el.on("change", this.onClick,  this);
40660     },
40661
40662
40663     getResizeEl : function(){
40664         return this.wrap;
40665     },
40666
40667     getPositionEl : function(){
40668         return this.wrap;
40669     },
40670
40671     // private
40672     onRender : function(ct, position){
40673         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40674         /*
40675         if(this.inputValue !== undefined){
40676             this.el.dom.value = this.inputValue;
40677         }
40678         */
40679         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40680         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40681         var viewEl = this.wrap.createChild({ 
40682             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40683         this.viewEl = viewEl;   
40684         this.wrap.on('click', this.onClick,  this); 
40685         
40686         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40687         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40688         
40689         
40690         
40691         if(this.boxLabel){
40692             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40693         //    viewEl.on('click', this.onClick,  this); 
40694         }
40695         //if(this.checked){
40696             this.setChecked(this.checked);
40697         //}else{
40698             //this.checked = this.el.dom;
40699         //}
40700
40701     },
40702
40703     // private
40704     initValue : Roo.emptyFn,
40705
40706     /**
40707      * Returns the checked state of the checkbox.
40708      * @return {Boolean} True if checked, else false
40709      */
40710     getValue : function(){
40711         if(this.el){
40712             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40713         }
40714         return this.valueOff;
40715         
40716     },
40717
40718         // private
40719     onClick : function(){ 
40720         this.setChecked(!this.checked);
40721
40722         //if(this.el.dom.checked != this.checked){
40723         //    this.setValue(this.el.dom.checked);
40724        // }
40725     },
40726
40727     /**
40728      * Sets the checked state of the checkbox.
40729      * On is always based on a string comparison between inputValue and the param.
40730      * @param {Boolean/String} value - the value to set 
40731      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40732      */
40733     setValue : function(v,suppressEvent){
40734         
40735         
40736         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40737         //if(this.el && this.el.dom){
40738         //    this.el.dom.checked = this.checked;
40739         //    this.el.dom.defaultChecked = this.checked;
40740         //}
40741         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40742         //this.fireEvent("check", this, this.checked);
40743     },
40744     // private..
40745     setChecked : function(state,suppressEvent)
40746     {
40747         if (this.inSetChecked) {
40748             this.checked = state;
40749             return;
40750         }
40751         
40752     
40753         if(this.wrap){
40754             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40755         }
40756         this.checked = state;
40757         if(suppressEvent !== true){
40758             this.fireEvent('check', this, state);
40759         }
40760         this.inSetChecked = true;
40761         this.el.dom.value = state ? this.inputValue : this.valueOff;
40762         this.inSetChecked = false;
40763         
40764     },
40765     // handle setting of hidden value by some other method!!?!?
40766     setFromHidden: function()
40767     {
40768         if(!this.el){
40769             return;
40770         }
40771         //console.log("SET FROM HIDDEN");
40772         //alert('setFrom hidden');
40773         this.setValue(this.el.dom.value);
40774     },
40775     
40776     onDestroy : function()
40777     {
40778         if(this.viewEl){
40779             Roo.get(this.viewEl).remove();
40780         }
40781          
40782         Roo.form.Checkbox.superclass.onDestroy.call(this);
40783     }
40784
40785 });/*
40786  * Based on:
40787  * Ext JS Library 1.1.1
40788  * Copyright(c) 2006-2007, Ext JS, LLC.
40789  *
40790  * Originally Released Under LGPL - original licence link has changed is not relivant.
40791  *
40792  * Fork - LGPL
40793  * <script type="text/javascript">
40794  */
40795  
40796 /**
40797  * @class Roo.form.Radio
40798  * @extends Roo.form.Checkbox
40799  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40800  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40801  * @constructor
40802  * Creates a new Radio
40803  * @param {Object} config Configuration options
40804  */
40805 Roo.form.Radio = function(){
40806     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40807 };
40808 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40809     inputType: 'radio',
40810
40811     /**
40812      * If this radio is part of a group, it will return the selected value
40813      * @return {String}
40814      */
40815     getGroupValue : function(){
40816         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40817     },
40818     
40819     
40820     onRender : function(ct, position){
40821         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40822         
40823         if(this.inputValue !== undefined){
40824             this.el.dom.value = this.inputValue;
40825         }
40826          
40827         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40828         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40829         //var viewEl = this.wrap.createChild({ 
40830         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40831         //this.viewEl = viewEl;   
40832         //this.wrap.on('click', this.onClick,  this); 
40833         
40834         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40835         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40836         
40837         
40838         
40839         if(this.boxLabel){
40840             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40841         //    viewEl.on('click', this.onClick,  this); 
40842         }
40843          if(this.checked){
40844             this.el.dom.checked =   'checked' ;
40845         }
40846          
40847     } 
40848     
40849     
40850 });//<script type="text/javascript">
40851
40852 /*
40853  * Based  Ext JS Library 1.1.1
40854  * Copyright(c) 2006-2007, Ext JS, LLC.
40855  * LGPL
40856  *
40857  */
40858  
40859 /**
40860  * @class Roo.HtmlEditorCore
40861  * @extends Roo.Component
40862  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
40863  *
40864  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40865  */
40866
40867 Roo.HtmlEditorCore = function(config){
40868     
40869     
40870     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
40871     this.addEvents({
40872         /**
40873          * @event initialize
40874          * Fires when the editor is fully initialized (including the iframe)
40875          * @param {Roo.HtmlEditorCore} this
40876          */
40877         initialize: true,
40878         /**
40879          * @event activate
40880          * Fires when the editor is first receives the focus. Any insertion must wait
40881          * until after this event.
40882          * @param {Roo.HtmlEditorCore} this
40883          */
40884         activate: true,
40885          /**
40886          * @event beforesync
40887          * Fires before the textarea is updated with content from the editor iframe. Return false
40888          * to cancel the sync.
40889          * @param {Roo.HtmlEditorCore} this
40890          * @param {String} html
40891          */
40892         beforesync: true,
40893          /**
40894          * @event beforepush
40895          * Fires before the iframe editor is updated with content from the textarea. Return false
40896          * to cancel the push.
40897          * @param {Roo.HtmlEditorCore} this
40898          * @param {String} html
40899          */
40900         beforepush: true,
40901          /**
40902          * @event sync
40903          * Fires when the textarea is updated with content from the editor iframe.
40904          * @param {Roo.HtmlEditorCore} this
40905          * @param {String} html
40906          */
40907         sync: true,
40908          /**
40909          * @event push
40910          * Fires when the iframe editor is updated with content from the textarea.
40911          * @param {Roo.HtmlEditorCore} this
40912          * @param {String} html
40913          */
40914         push: true,
40915         
40916         /**
40917          * @event editorevent
40918          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40919          * @param {Roo.HtmlEditorCore} this
40920          */
40921         editorevent: true
40922     });
40923      
40924 };
40925
40926
40927 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
40928
40929
40930      /**
40931      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
40932      */
40933     
40934     owner : false,
40935     
40936      /**
40937      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40938      *                        Roo.resizable.
40939      */
40940     resizable : false,
40941      /**
40942      * @cfg {Number} height (in pixels)
40943      */   
40944     height: 300,
40945    /**
40946      * @cfg {Number} width (in pixels)
40947      */   
40948     width: 500,
40949     
40950     /**
40951      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40952      * 
40953      */
40954     stylesheets: false,
40955     
40956     // id of frame..
40957     frameId: false,
40958     
40959     // private properties
40960     validationEvent : false,
40961     deferHeight: true,
40962     initialized : false,
40963     activated : false,
40964     sourceEditMode : false,
40965     onFocus : Roo.emptyFn,
40966     iframePad:3,
40967     hideMode:'offsets',
40968     
40969      
40970     
40971
40972     /**
40973      * Protected method that will not generally be called directly. It
40974      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40975      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40976      */
40977     getDocMarkup : function(){
40978         // body styles..
40979         var st = '';
40980         Roo.log(this.stylesheets);
40981         
40982         // inherit styels from page...?? 
40983         if (this.stylesheets === false) {
40984             
40985             Roo.get(document.head).select('style').each(function(node) {
40986                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40987             });
40988             
40989             Roo.get(document.head).select('link').each(function(node) { 
40990                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40991             });
40992             
40993         } else if (!this.stylesheets.length) {
40994                 // simple..
40995                 st = '<style type="text/css">' +
40996                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40997                    '</style>';
40998         } else {
40999             Roo.each(this.stylesheets, function(s) {
41000                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41001             });
41002             
41003         }
41004         
41005         st +=  '<style type="text/css">' +
41006             'IMG { cursor: pointer } ' +
41007         '</style>';
41008
41009         
41010         return '<html><head>' + st  +
41011             //<style type="text/css">' +
41012             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41013             //'</style>' +
41014             ' </head><body class="roo-htmleditor-body"></body></html>';
41015     },
41016
41017     // private
41018     onRender : function(ct, position)
41019     {
41020         var _t = this;
41021         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41022         this.el = this.owner.el;
41023         
41024         
41025         this.el.dom.style.border = '0 none';
41026         this.el.dom.setAttribute('tabIndex', -1);
41027         this.el.addClass('x-hidden');
41028         
41029         
41030         
41031         if(Roo.isIE){ // fix IE 1px bogus margin
41032             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41033         }
41034        
41035         
41036         this.frameId = Roo.id();
41037         
41038          
41039         
41040         var iframe = this.owner.wrap.createChild({
41041             tag: 'iframe',
41042             id: this.frameId,
41043             name: this.frameId,
41044             frameBorder : 'no',
41045             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41046         }, this.el
41047         );
41048         
41049         
41050         this.iframe = iframe.dom;
41051
41052          this.assignDocWin();
41053         
41054         this.doc.designMode = 'on';
41055        
41056         this.doc.open();
41057         this.doc.write(this.getDocMarkup());
41058         this.doc.close();
41059
41060         
41061         var task = { // must defer to wait for browser to be ready
41062             run : function(){
41063                 //console.log("run task?" + this.doc.readyState);
41064                 this.assignDocWin();
41065                 if(this.doc.body || this.doc.readyState == 'complete'){
41066                     try {
41067                         this.doc.designMode="on";
41068                     } catch (e) {
41069                         return;
41070                     }
41071                     Roo.TaskMgr.stop(task);
41072                     this.initEditor.defer(10, this);
41073                 }
41074             },
41075             interval : 10,
41076             duration: 10000,
41077             scope: this
41078         };
41079         Roo.TaskMgr.start(task);
41080
41081         
41082          
41083     },
41084
41085     // private
41086     onResize : function(w, h)
41087     {
41088         //Roo.log('resize: ' +w + ',' + h );
41089         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41090         if(!this.iframe){
41091             return;
41092         }
41093         if(typeof w == 'number'){
41094             
41095             this.iframe.style.width = w + 'px';
41096         }
41097         if(typeof h == 'number'){
41098             
41099             this.iframe.style.height = h + 'px';
41100             if(this.doc){
41101                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41102             }
41103         }
41104         
41105     },
41106
41107     /**
41108      * Toggles the editor between standard and source edit mode.
41109      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41110      */
41111     toggleSourceEdit : function(sourceEditMode){
41112         
41113         this.sourceEditMode = sourceEditMode === true;
41114         
41115         if(this.sourceEditMode){
41116  
41117             this.iframe.className = 'x-hidden';     //FIXME - what's the BS styles for these
41118             
41119         }else{
41120  
41121             this.iframe.className = '';
41122             this.deferFocus();
41123         }
41124         //this.setSize(this.owner.wrap.getSize());
41125         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41126     },
41127
41128     
41129   
41130
41131     /**
41132      * Protected method that will not generally be called directly. If you need/want
41133      * custom HTML cleanup, this is the method you should override.
41134      * @param {String} html The HTML to be cleaned
41135      * return {String} The cleaned HTML
41136      */
41137     cleanHtml : function(html){
41138         html = String(html);
41139         if(html.length > 5){
41140             if(Roo.isSafari){ // strip safari nonsense
41141                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41142             }
41143         }
41144         if(html == '&nbsp;'){
41145             html = '';
41146         }
41147         return html;
41148     },
41149
41150     /**
41151      * HTML Editor -> Textarea
41152      * Protected method that will not generally be called directly. Syncs the contents
41153      * of the editor iframe with the textarea.
41154      */
41155     syncValue : function(){
41156         if(this.initialized){
41157             var bd = (this.doc.body || this.doc.documentElement);
41158             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41159             var html = bd.innerHTML;
41160             if(Roo.isSafari){
41161                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41162                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41163                 if(m && m[1]){
41164                     html = '<div style="'+m[0]+'">' + html + '</div>';
41165                 }
41166             }
41167             html = this.cleanHtml(html);
41168             // fix up the special chars.. normaly like back quotes in word...
41169             // however we do not want to do this with chinese..
41170             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41171                 var cc = b.charCodeAt();
41172                 if (
41173                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41174                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41175                     (cc >= 0xf900 && cc < 0xfb00 )
41176                 ) {
41177                         return b;
41178                 }
41179                 return "&#"+cc+";" 
41180             });
41181             if(this.owner.fireEvent('beforesync', this, html) !== false){
41182                 this.el.dom.value = html;
41183                 this.owner.fireEvent('sync', this, html);
41184             }
41185         }
41186     },
41187
41188     /**
41189      * Protected method that will not generally be called directly. Pushes the value of the textarea
41190      * into the iframe editor.
41191      */
41192     pushValue : function(){
41193         if(this.initialized){
41194             var v = this.el.dom.value;
41195             
41196             if(v.length < 1){
41197                 v = '&#160;';
41198             }
41199             
41200             if(this.owner.fireEvent('beforepush', this, v) !== false){
41201                 var d = (this.doc.body || this.doc.documentElement);
41202                 d.innerHTML = v;
41203                 this.cleanUpPaste();
41204                 this.el.dom.value = d.innerHTML;
41205                 this.owner.fireEvent('push', this, v);
41206             }
41207         }
41208     },
41209
41210     // private
41211     deferFocus : function(){
41212         this.focus.defer(10, this);
41213     },
41214
41215     // doc'ed in Field
41216     focus : function(){
41217         if(this.win && !this.sourceEditMode){
41218             this.win.focus();
41219         }else{
41220             this.el.focus();
41221         }
41222     },
41223     
41224     assignDocWin: function()
41225     {
41226         var iframe = this.iframe;
41227         
41228          if(Roo.isIE){
41229             this.doc = iframe.contentWindow.document;
41230             this.win = iframe.contentWindow;
41231         } else {
41232             if (!Roo.get(this.frameId)) {
41233                 return;
41234             }
41235             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41236             this.win = Roo.get(this.frameId).dom.contentWindow;
41237         }
41238     },
41239     
41240     // private
41241     initEditor : function(){
41242         //console.log("INIT EDITOR");
41243         this.assignDocWin();
41244         
41245         
41246         
41247         this.doc.designMode="on";
41248         this.doc.open();
41249         this.doc.write(this.getDocMarkup());
41250         this.doc.close();
41251         
41252         var dbody = (this.doc.body || this.doc.documentElement);
41253         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41254         // this copies styles from the containing element into thsi one..
41255         // not sure why we need all of this..
41256         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41257         ss['background-attachment'] = 'fixed'; // w3c
41258         dbody.bgProperties = 'fixed'; // ie
41259         Roo.DomHelper.applyStyles(dbody, ss);
41260         Roo.EventManager.on(this.doc, {
41261             //'mousedown': this.onEditorEvent,
41262             'mouseup': this.onEditorEvent,
41263             'dblclick': this.onEditorEvent,
41264             'click': this.onEditorEvent,
41265             'keyup': this.onEditorEvent,
41266             buffer:100,
41267             scope: this
41268         });
41269         if(Roo.isGecko){
41270             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41271         }
41272         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41273             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41274         }
41275         this.initialized = true;
41276
41277         this.owner.fireEvent('initialize', this);
41278         this.pushValue();
41279         
41280         if(this.autosave){
41281             this.save = setInterval(function(){
41282             this.syncValue();
41283             if(!this.isDirty()){
41284                 Roo.log('not dirty');
41285                 return;
41286             }
41287             Roo.log('dirty, auto save!');
41288 //
41289 //            _this.form.findField('body').originalValue = _this.form.findField('body').getValue();
41290         }, 1000);
41291         }
41292     },
41293
41294     // private
41295     onDestroy : function(){
41296         
41297         
41298         
41299         if(this.rendered){
41300             
41301             //for (var i =0; i < this.toolbars.length;i++) {
41302             //    // fixme - ask toolbars for heights?
41303             //    this.toolbars[i].onDestroy();
41304            // }
41305             
41306             //this.wrap.dom.innerHTML = '';
41307             //this.wrap.remove();
41308         }
41309     },
41310
41311     // private
41312     onFirstFocus : function(){
41313         
41314         this.assignDocWin();
41315         
41316         
41317         this.activated = true;
41318          
41319     
41320         if(Roo.isGecko){ // prevent silly gecko errors
41321             this.win.focus();
41322             var s = this.win.getSelection();
41323             if(!s.focusNode || s.focusNode.nodeType != 3){
41324                 var r = s.getRangeAt(0);
41325                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41326                 r.collapse(true);
41327                 this.deferFocus();
41328             }
41329             try{
41330                 this.execCmd('useCSS', true);
41331                 this.execCmd('styleWithCSS', false);
41332             }catch(e){}
41333         }
41334         this.owner.fireEvent('activate', this);
41335     },
41336
41337     // private
41338     adjustFont: function(btn){
41339         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41340         //if(Roo.isSafari){ // safari
41341         //    adjust *= 2;
41342        // }
41343         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41344         if(Roo.isSafari){ // safari
41345             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41346             v =  (v < 10) ? 10 : v;
41347             v =  (v > 48) ? 48 : v;
41348             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41349             
41350         }
41351         
41352         
41353         v = Math.max(1, v+adjust);
41354         
41355         this.execCmd('FontSize', v  );
41356     },
41357
41358     onEditorEvent : function(e){
41359         this.owner.fireEvent('editorevent', this, e);
41360       //  this.updateToolbar();
41361         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41362     },
41363
41364     insertTag : function(tg)
41365     {
41366         // could be a bit smarter... -> wrap the current selected tRoo..
41367         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41368             
41369             range = this.createRange(this.getSelection());
41370             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41371             wrappingNode.appendChild(range.extractContents());
41372             range.insertNode(wrappingNode);
41373
41374             return;
41375             
41376             
41377             
41378         }
41379         this.execCmd("formatblock",   tg);
41380         
41381     },
41382     
41383     insertText : function(txt)
41384     {
41385         
41386         
41387         var range = this.createRange();
41388         range.deleteContents();
41389                //alert(Sender.getAttribute('label'));
41390                
41391         range.insertNode(this.doc.createTextNode(txt));
41392     } ,
41393     
41394      
41395
41396     /**
41397      * Executes a Midas editor command on the editor document and performs necessary focus and
41398      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41399      * @param {String} cmd The Midas command
41400      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41401      */
41402     relayCmd : function(cmd, value){
41403         this.win.focus();
41404         this.execCmd(cmd, value);
41405         this.owner.fireEvent('editorevent', this);
41406         //this.updateToolbar();
41407         this.owner.deferFocus();
41408     },
41409
41410     /**
41411      * Executes a Midas editor command directly on the editor document.
41412      * For visual commands, you should use {@link #relayCmd} instead.
41413      * <b>This should only be called after the editor is initialized.</b>
41414      * @param {String} cmd The Midas command
41415      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41416      */
41417     execCmd : function(cmd, value){
41418         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41419         this.syncValue();
41420     },
41421  
41422  
41423    
41424     /**
41425      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41426      * to insert tRoo.
41427      * @param {String} text | dom node.. 
41428      */
41429     insertAtCursor : function(text)
41430     {
41431         
41432         
41433         
41434         if(!this.activated){
41435             return;
41436         }
41437         /*
41438         if(Roo.isIE){
41439             this.win.focus();
41440             var r = this.doc.selection.createRange();
41441             if(r){
41442                 r.collapse(true);
41443                 r.pasteHTML(text);
41444                 this.syncValue();
41445                 this.deferFocus();
41446             
41447             }
41448             return;
41449         }
41450         */
41451         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41452             this.win.focus();
41453             
41454             
41455             // from jquery ui (MIT licenced)
41456             var range, node;
41457             var win = this.win;
41458             
41459             if (win.getSelection && win.getSelection().getRangeAt) {
41460                 range = win.getSelection().getRangeAt(0);
41461                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41462                 range.insertNode(node);
41463             } else if (win.document.selection && win.document.selection.createRange) {
41464                 // no firefox support
41465                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41466                 win.document.selection.createRange().pasteHTML(txt);
41467             } else {
41468                 // no firefox support
41469                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41470                 this.execCmd('InsertHTML', txt);
41471             } 
41472             
41473             this.syncValue();
41474             
41475             this.deferFocus();
41476         }
41477     },
41478  // private
41479     mozKeyPress : function(e){
41480         if(e.ctrlKey){
41481             var c = e.getCharCode(), cmd;
41482           
41483             if(c > 0){
41484                 c = String.fromCharCode(c).toLowerCase();
41485                 switch(c){
41486                     case 'b':
41487                         cmd = 'bold';
41488                         break;
41489                     case 'i':
41490                         cmd = 'italic';
41491                         break;
41492                     
41493                     case 'u':
41494                         cmd = 'underline';
41495                         break;
41496                     
41497                     case 'v':
41498                         this.cleanUpPaste.defer(100, this);
41499                         return;
41500                         
41501                 }
41502                 if(cmd){
41503                     this.win.focus();
41504                     this.execCmd(cmd);
41505                     this.deferFocus();
41506                     e.preventDefault();
41507                 }
41508                 
41509             }
41510         }
41511     },
41512
41513     // private
41514     fixKeys : function(){ // load time branching for fastest keydown performance
41515         if(Roo.isIE){
41516             return function(e){
41517                 var k = e.getKey(), r;
41518                 if(k == e.TAB){
41519                     e.stopEvent();
41520                     r = this.doc.selection.createRange();
41521                     if(r){
41522                         r.collapse(true);
41523                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41524                         this.deferFocus();
41525                     }
41526                     return;
41527                 }
41528                 
41529                 if(k == e.ENTER){
41530                     r = this.doc.selection.createRange();
41531                     if(r){
41532                         var target = r.parentElement();
41533                         if(!target || target.tagName.toLowerCase() != 'li'){
41534                             e.stopEvent();
41535                             r.pasteHTML('<br />');
41536                             r.collapse(false);
41537                             r.select();
41538                         }
41539                     }
41540                 }
41541                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41542                     this.cleanUpPaste.defer(100, this);
41543                     return;
41544                 }
41545                 
41546                 
41547             };
41548         }else if(Roo.isOpera){
41549             return function(e){
41550                 var k = e.getKey();
41551                 if(k == e.TAB){
41552                     e.stopEvent();
41553                     this.win.focus();
41554                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41555                     this.deferFocus();
41556                 }
41557                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41558                     this.cleanUpPaste.defer(100, this);
41559                     return;
41560                 }
41561                 
41562             };
41563         }else if(Roo.isSafari){
41564             return function(e){
41565                 var k = e.getKey();
41566                 
41567                 if(k == e.TAB){
41568                     e.stopEvent();
41569                     this.execCmd('InsertText','\t');
41570                     this.deferFocus();
41571                     return;
41572                 }
41573                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41574                     this.cleanUpPaste.defer(100, this);
41575                     return;
41576                 }
41577                 
41578              };
41579         }
41580     }(),
41581     
41582     getAllAncestors: function()
41583     {
41584         var p = this.getSelectedNode();
41585         var a = [];
41586         if (!p) {
41587             a.push(p); // push blank onto stack..
41588             p = this.getParentElement();
41589         }
41590         
41591         
41592         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41593             a.push(p);
41594             p = p.parentNode;
41595         }
41596         a.push(this.doc.body);
41597         return a;
41598     },
41599     lastSel : false,
41600     lastSelNode : false,
41601     
41602     
41603     getSelection : function() 
41604     {
41605         this.assignDocWin();
41606         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41607     },
41608     
41609     getSelectedNode: function() 
41610     {
41611         // this may only work on Gecko!!!
41612         
41613         // should we cache this!!!!
41614         
41615         
41616         
41617          
41618         var range = this.createRange(this.getSelection()).cloneRange();
41619         
41620         if (Roo.isIE) {
41621             var parent = range.parentElement();
41622             while (true) {
41623                 var testRange = range.duplicate();
41624                 testRange.moveToElementText(parent);
41625                 if (testRange.inRange(range)) {
41626                     break;
41627                 }
41628                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41629                     break;
41630                 }
41631                 parent = parent.parentElement;
41632             }
41633             return parent;
41634         }
41635         
41636         // is ancestor a text element.
41637         var ac =  range.commonAncestorContainer;
41638         if (ac.nodeType == 3) {
41639             ac = ac.parentNode;
41640         }
41641         
41642         var ar = ac.childNodes;
41643          
41644         var nodes = [];
41645         var other_nodes = [];
41646         var has_other_nodes = false;
41647         for (var i=0;i<ar.length;i++) {
41648             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41649                 continue;
41650             }
41651             // fullly contained node.
41652             
41653             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41654                 nodes.push(ar[i]);
41655                 continue;
41656             }
41657             
41658             // probably selected..
41659             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41660                 other_nodes.push(ar[i]);
41661                 continue;
41662             }
41663             // outer..
41664             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41665                 continue;
41666             }
41667             
41668             
41669             has_other_nodes = true;
41670         }
41671         if (!nodes.length && other_nodes.length) {
41672             nodes= other_nodes;
41673         }
41674         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41675             return false;
41676         }
41677         
41678         return nodes[0];
41679     },
41680     createRange: function(sel)
41681     {
41682         // this has strange effects when using with 
41683         // top toolbar - not sure if it's a great idea.
41684         //this.editor.contentWindow.focus();
41685         if (typeof sel != "undefined") {
41686             try {
41687                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41688             } catch(e) {
41689                 return this.doc.createRange();
41690             }
41691         } else {
41692             return this.doc.createRange();
41693         }
41694     },
41695     getParentElement: function()
41696     {
41697         
41698         this.assignDocWin();
41699         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41700         
41701         var range = this.createRange(sel);
41702          
41703         try {
41704             var p = range.commonAncestorContainer;
41705             while (p.nodeType == 3) { // text node
41706                 p = p.parentNode;
41707             }
41708             return p;
41709         } catch (e) {
41710             return null;
41711         }
41712     
41713     },
41714     /***
41715      *
41716      * Range intersection.. the hard stuff...
41717      *  '-1' = before
41718      *  '0' = hits..
41719      *  '1' = after.
41720      *         [ -- selected range --- ]
41721      *   [fail]                        [fail]
41722      *
41723      *    basically..
41724      *      if end is before start or  hits it. fail.
41725      *      if start is after end or hits it fail.
41726      *
41727      *   if either hits (but other is outside. - then it's not 
41728      *   
41729      *    
41730      **/
41731     
41732     
41733     // @see http://www.thismuchiknow.co.uk/?p=64.
41734     rangeIntersectsNode : function(range, node)
41735     {
41736         var nodeRange = node.ownerDocument.createRange();
41737         try {
41738             nodeRange.selectNode(node);
41739         } catch (e) {
41740             nodeRange.selectNodeContents(node);
41741         }
41742     
41743         var rangeStartRange = range.cloneRange();
41744         rangeStartRange.collapse(true);
41745     
41746         var rangeEndRange = range.cloneRange();
41747         rangeEndRange.collapse(false);
41748     
41749         var nodeStartRange = nodeRange.cloneRange();
41750         nodeStartRange.collapse(true);
41751     
41752         var nodeEndRange = nodeRange.cloneRange();
41753         nodeEndRange.collapse(false);
41754     
41755         return rangeStartRange.compareBoundaryPoints(
41756                  Range.START_TO_START, nodeEndRange) == -1 &&
41757                rangeEndRange.compareBoundaryPoints(
41758                  Range.START_TO_START, nodeStartRange) == 1;
41759         
41760          
41761     },
41762     rangeCompareNode : function(range, node)
41763     {
41764         var nodeRange = node.ownerDocument.createRange();
41765         try {
41766             nodeRange.selectNode(node);
41767         } catch (e) {
41768             nodeRange.selectNodeContents(node);
41769         }
41770         
41771         
41772         range.collapse(true);
41773     
41774         nodeRange.collapse(true);
41775      
41776         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41777         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41778          
41779         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41780         
41781         var nodeIsBefore   =  ss == 1;
41782         var nodeIsAfter    = ee == -1;
41783         
41784         if (nodeIsBefore && nodeIsAfter)
41785             return 0; // outer
41786         if (!nodeIsBefore && nodeIsAfter)
41787             return 1; //right trailed.
41788         
41789         if (nodeIsBefore && !nodeIsAfter)
41790             return 2;  // left trailed.
41791         // fully contined.
41792         return 3;
41793     },
41794
41795     // private? - in a new class?
41796     cleanUpPaste :  function()
41797     {
41798         // cleans up the whole document..
41799          Roo.log('cleanuppaste');
41800         this.cleanUpChildren(this.doc.body);
41801         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41802         if (clean != this.doc.body.innerHTML) {
41803             this.doc.body.innerHTML = clean;
41804         }
41805         
41806     },
41807     
41808     cleanWordChars : function(input) {// change the chars to hex code
41809         var he = Roo.HtmlEditorCore;
41810         
41811         var output = input;
41812         Roo.each(he.swapCodes, function(sw) { 
41813             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41814             
41815             output = output.replace(swapper, sw[1]);
41816         });
41817         
41818         return output;
41819     },
41820     
41821     
41822     cleanUpChildren : function (n)
41823     {
41824         if (!n.childNodes.length) {
41825             return;
41826         }
41827         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41828            this.cleanUpChild(n.childNodes[i]);
41829         }
41830     },
41831     
41832     
41833         
41834     
41835     cleanUpChild : function (node)
41836     {
41837         var ed = this;
41838         //console.log(node);
41839         if (node.nodeName == "#text") {
41840             // clean up silly Windows -- stuff?
41841             return; 
41842         }
41843         if (node.nodeName == "#comment") {
41844             node.parentNode.removeChild(node);
41845             // clean up silly Windows -- stuff?
41846             return; 
41847         }
41848         
41849         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
41850             // remove node.
41851             node.parentNode.removeChild(node);
41852             return;
41853             
41854         }
41855         
41856         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41857         
41858         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41859         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41860         
41861         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41862         //    remove_keep_children = true;
41863         //}
41864         
41865         if (remove_keep_children) {
41866             this.cleanUpChildren(node);
41867             // inserts everything just before this node...
41868             while (node.childNodes.length) {
41869                 var cn = node.childNodes[0];
41870                 node.removeChild(cn);
41871                 node.parentNode.insertBefore(cn, node);
41872             }
41873             node.parentNode.removeChild(node);
41874             return;
41875         }
41876         
41877         if (!node.attributes || !node.attributes.length) {
41878             this.cleanUpChildren(node);
41879             return;
41880         }
41881         
41882         function cleanAttr(n,v)
41883         {
41884             
41885             if (v.match(/^\./) || v.match(/^\//)) {
41886                 return;
41887             }
41888             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41889                 return;
41890             }
41891             if (v.match(/^#/)) {
41892                 return;
41893             }
41894 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41895             node.removeAttribute(n);
41896             
41897         }
41898         
41899         function cleanStyle(n,v)
41900         {
41901             if (v.match(/expression/)) { //XSS?? should we even bother..
41902                 node.removeAttribute(n);
41903                 return;
41904             }
41905             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
41906             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
41907             
41908             
41909             var parts = v.split(/;/);
41910             var clean = [];
41911             
41912             Roo.each(parts, function(p) {
41913                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41914                 if (!p.length) {
41915                     return true;
41916                 }
41917                 var l = p.split(':').shift().replace(/\s+/g,'');
41918                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41919                 
41920                 
41921                 if ( cblack.indexOf(l) > -1) {
41922 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41923                     //node.removeAttribute(n);
41924                     return true;
41925                 }
41926                 //Roo.log()
41927                 // only allow 'c whitelisted system attributes'
41928                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41929 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41930                     //node.removeAttribute(n);
41931                     return true;
41932                 }
41933                 
41934                 
41935                  
41936                 
41937                 clean.push(p);
41938                 return true;
41939             });
41940             if (clean.length) { 
41941                 node.setAttribute(n, clean.join(';'));
41942             } else {
41943                 node.removeAttribute(n);
41944             }
41945             
41946         }
41947         
41948         
41949         for (var i = node.attributes.length-1; i > -1 ; i--) {
41950             var a = node.attributes[i];
41951             //console.log(a);
41952             
41953             if (a.name.toLowerCase().substr(0,2)=='on')  {
41954                 node.removeAttribute(a.name);
41955                 continue;
41956             }
41957             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
41958                 node.removeAttribute(a.name);
41959                 continue;
41960             }
41961             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
41962                 cleanAttr(a.name,a.value); // fixme..
41963                 continue;
41964             }
41965             if (a.name == 'style') {
41966                 cleanStyle(a.name,a.value);
41967                 continue;
41968             }
41969             /// clean up MS crap..
41970             // tecnically this should be a list of valid class'es..
41971             
41972             
41973             if (a.name == 'class') {
41974                 if (a.value.match(/^Mso/)) {
41975                     node.className = '';
41976                 }
41977                 
41978                 if (a.value.match(/body/)) {
41979                     node.className = '';
41980                 }
41981                 continue;
41982             }
41983             
41984             // style cleanup!?
41985             // class cleanup?
41986             
41987         }
41988         
41989         
41990         this.cleanUpChildren(node);
41991         
41992         
41993     }
41994     
41995     
41996     // hide stuff that is not compatible
41997     /**
41998      * @event blur
41999      * @hide
42000      */
42001     /**
42002      * @event change
42003      * @hide
42004      */
42005     /**
42006      * @event focus
42007      * @hide
42008      */
42009     /**
42010      * @event specialkey
42011      * @hide
42012      */
42013     /**
42014      * @cfg {String} fieldClass @hide
42015      */
42016     /**
42017      * @cfg {String} focusClass @hide
42018      */
42019     /**
42020      * @cfg {String} autoCreate @hide
42021      */
42022     /**
42023      * @cfg {String} inputType @hide
42024      */
42025     /**
42026      * @cfg {String} invalidClass @hide
42027      */
42028     /**
42029      * @cfg {String} invalidText @hide
42030      */
42031     /**
42032      * @cfg {String} msgFx @hide
42033      */
42034     /**
42035      * @cfg {String} validateOnBlur @hide
42036      */
42037 });
42038
42039 Roo.HtmlEditorCore.white = [
42040         'area', 'br', 'img', 'input', 'hr', 'wbr',
42041         
42042        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42043        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42044        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42045        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42046        'table',   'ul',         'xmp', 
42047        
42048        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42049       'thead',   'tr', 
42050      
42051       'dir', 'menu', 'ol', 'ul', 'dl',
42052        
42053       'embed',  'object'
42054 ];
42055
42056
42057 Roo.HtmlEditorCore.black = [
42058     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42059         'applet', // 
42060         'base',   'basefont', 'bgsound', 'blink',  'body', 
42061         'frame',  'frameset', 'head',    'html',   'ilayer', 
42062         'iframe', 'layer',  'link',     'meta',    'object',   
42063         'script', 'style' ,'title',  'xml' // clean later..
42064 ];
42065 Roo.HtmlEditorCore.clean = [
42066     'script', 'style', 'title', 'xml'
42067 ];
42068 Roo.HtmlEditorCore.remove = [
42069     'font'
42070 ];
42071 // attributes..
42072
42073 Roo.HtmlEditorCore.ablack = [
42074     'on'
42075 ];
42076     
42077 Roo.HtmlEditorCore.aclean = [ 
42078     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42079 ];
42080
42081 // protocols..
42082 Roo.HtmlEditorCore.pwhite= [
42083         'http',  'https',  'mailto'
42084 ];
42085
42086 // white listed style attributes.
42087 Roo.HtmlEditorCore.cwhite= [
42088       //  'text-align', /// default is to allow most things..
42089       
42090          
42091 //        'font-size'//??
42092 ];
42093
42094 // black listed style attributes.
42095 Roo.HtmlEditorCore.cblack= [
42096       //  'font-size' -- this can be set by the project 
42097 ];
42098
42099
42100 Roo.HtmlEditorCore.swapCodes   =[ 
42101     [    8211, "--" ], 
42102     [    8212, "--" ], 
42103     [    8216,  "'" ],  
42104     [    8217, "'" ],  
42105     [    8220, '"' ],  
42106     [    8221, '"' ],  
42107     [    8226, "*" ],  
42108     [    8230, "..." ]
42109 ]; 
42110
42111     //<script type="text/javascript">
42112
42113 /*
42114  * Ext JS Library 1.1.1
42115  * Copyright(c) 2006-2007, Ext JS, LLC.
42116  * Licence LGPL
42117  * 
42118  */
42119  
42120  
42121 Roo.form.HtmlEditor = function(config){
42122     
42123     
42124     
42125     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42126     
42127     if (!this.toolbars) {
42128         this.toolbars = [];
42129     }
42130     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42131     
42132     
42133 };
42134
42135 /**
42136  * @class Ext.form.HtmlEditor
42137  * @extends Ext.form.Field
42138  * Provides a lightweight HTML Editor component.
42139  *
42140  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42141  * 
42142  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42143  * supported by this editor.</b><br/><br/>
42144  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42145  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42146  */
42147 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42148       /**
42149      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42150      */
42151     toolbars : false,
42152    
42153      /**
42154      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42155      *                        Roo.resizable.
42156      */
42157     resizable : false,
42158      /**
42159      * @cfg {Number} height (in pixels)
42160      */   
42161     height: 300,
42162    /**
42163      * @cfg {Number} width (in pixels)
42164      */   
42165     width: 500,
42166     
42167     /**
42168      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42169      * 
42170      */
42171     stylesheets: false,
42172     
42173     /**
42174      * @cfg {Boolean} autosave Auto save the field as a file (default false)
42175      */
42176     
42177     autosave : false,
42178     
42179     // id of frame..
42180     frameId: false,
42181     
42182     // private properties
42183     validationEvent : false,
42184     deferHeight: true,
42185     initialized : false,
42186     activated : false,
42187     
42188     onFocus : Roo.emptyFn,
42189     iframePad:3,
42190     hideMode:'offsets',
42191     
42192     defaultAutoCreate : { // modified by initCompnoent..
42193         tag: "textarea",
42194         style:"width:500px;height:300px;",
42195         autocomplete: "off"
42196     },
42197
42198     // private
42199     initComponent : function(){
42200         this.addEvents({
42201             /**
42202              * @event initialize
42203              * Fires when the editor is fully initialized (including the iframe)
42204              * @param {HtmlEditor} this
42205              */
42206             initialize: true,
42207             /**
42208              * @event activate
42209              * Fires when the editor is first receives the focus. Any insertion must wait
42210              * until after this event.
42211              * @param {HtmlEditor} this
42212              */
42213             activate: true,
42214              /**
42215              * @event beforesync
42216              * Fires before the textarea is updated with content from the editor iframe. Return false
42217              * to cancel the sync.
42218              * @param {HtmlEditor} this
42219              * @param {String} html
42220              */
42221             beforesync: true,
42222              /**
42223              * @event beforepush
42224              * Fires before the iframe editor is updated with content from the textarea. Return false
42225              * to cancel the push.
42226              * @param {HtmlEditor} this
42227              * @param {String} html
42228              */
42229             beforepush: true,
42230              /**
42231              * @event sync
42232              * Fires when the textarea is updated with content from the editor iframe.
42233              * @param {HtmlEditor} this
42234              * @param {String} html
42235              */
42236             sync: true,
42237              /**
42238              * @event push
42239              * Fires when the iframe editor is updated with content from the textarea.
42240              * @param {HtmlEditor} this
42241              * @param {String} html
42242              */
42243             push: true,
42244              /**
42245              * @event editmodechange
42246              * Fires when the editor switches edit modes
42247              * @param {HtmlEditor} this
42248              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42249              */
42250             editmodechange: true,
42251             /**
42252              * @event editorevent
42253              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42254              * @param {HtmlEditor} this
42255              */
42256             editorevent: true,
42257             /**
42258              * @event firstfocus
42259              * Fires when on first focus - needed by toolbars..
42260              * @param {HtmlEditor} this
42261              */
42262             firstfocus: true
42263         });
42264         this.defaultAutoCreate =  {
42265             tag: "textarea",
42266             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42267             autocomplete: "off"
42268         };
42269     },
42270
42271     /**
42272      * Protected method that will not generally be called directly. It
42273      * is called when the editor creates its toolbar. Override this method if you need to
42274      * add custom toolbar buttons.
42275      * @param {HtmlEditor} editor
42276      */
42277     createToolbar : function(editor){
42278         Roo.log("create toolbars");
42279         if (!editor.toolbars || !editor.toolbars.length) {
42280             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42281         }
42282         
42283         for (var i =0 ; i < editor.toolbars.length;i++) {
42284             editor.toolbars[i] = Roo.factory(
42285                     typeof(editor.toolbars[i]) == 'string' ?
42286                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42287                 Roo.form.HtmlEditor);
42288             editor.toolbars[i].init(editor);
42289         }
42290          
42291         
42292     },
42293
42294      
42295     // private
42296     onRender : function(ct, position)
42297     {
42298         var _t = this;
42299         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42300         
42301         this.wrap = this.el.wrap({
42302             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42303         });
42304         
42305         this.editorcore.onRender(ct, position);
42306          
42307         if (this.resizable) {
42308             this.resizeEl = new Roo.Resizable(this.wrap, {
42309                 pinned : true,
42310                 wrap: true,
42311                 dynamic : true,
42312                 minHeight : this.height,
42313                 height: this.height,
42314                 handles : this.resizable,
42315                 width: this.width,
42316                 listeners : {
42317                     resize : function(r, w, h) {
42318                         _t.onResize(w,h); // -something
42319                     }
42320                 }
42321             });
42322             
42323         }
42324         this.createToolbar(this);
42325        
42326         
42327         if(!this.width){
42328             this.setSize(this.wrap.getSize());
42329         }
42330         if (this.resizeEl) {
42331             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42332             // should trigger onReize..
42333         }
42334     },
42335
42336     // private
42337     onResize : function(w, h)
42338     {
42339         //Roo.log('resize: ' +w + ',' + h );
42340         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42341         var ew = false;
42342         var eh = false;
42343         
42344         if(this.el ){
42345             if(typeof w == 'number'){
42346                 var aw = w - this.wrap.getFrameWidth('lr');
42347                 this.el.setWidth(this.adjustWidth('textarea', aw));
42348                 ew = aw;
42349             }
42350             if(typeof h == 'number'){
42351                 var tbh = 0;
42352                 for (var i =0; i < this.toolbars.length;i++) {
42353                     // fixme - ask toolbars for heights?
42354                     tbh += this.toolbars[i].tb.el.getHeight();
42355                     if (this.toolbars[i].footer) {
42356                         tbh += this.toolbars[i].footer.el.getHeight();
42357                     }
42358                 }
42359                 
42360                 
42361                 
42362                 
42363                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42364                 ah -= 5; // knock a few pixes off for look..
42365                 this.el.setHeight(this.adjustWidth('textarea', ah));
42366                 var eh = ah;
42367             }
42368         }
42369         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42370         this.editorcore.onResize(ew,eh);
42371         
42372     },
42373
42374     /**
42375      * Toggles the editor between standard and source edit mode.
42376      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42377      */
42378     toggleSourceEdit : function(sourceEditMode)
42379     {
42380         this.editorcore.toggleSourceEdit(sourceEditMode);
42381         
42382         if(this.editorcore.sourceEditMode){
42383             Roo.log('editor - showing textarea');
42384             
42385 //            Roo.log('in');
42386 //            Roo.log(this.syncValue());
42387             this.editorcore.syncValue();
42388             this.el.removeClass('x-hidden');
42389             this.el.dom.removeAttribute('tabIndex');
42390             this.el.focus();
42391         }else{
42392             Roo.log('editor - hiding textarea');
42393 //            Roo.log('out')
42394 //            Roo.log(this.pushValue()); 
42395             this.editorcore.pushValue();
42396             
42397             this.el.addClass('x-hidden');
42398             this.el.dom.setAttribute('tabIndex', -1);
42399             //this.deferFocus();
42400         }
42401          
42402         this.setSize(this.wrap.getSize());
42403         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42404     },
42405  
42406     // private (for BoxComponent)
42407     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42408
42409     // private (for BoxComponent)
42410     getResizeEl : function(){
42411         return this.wrap;
42412     },
42413
42414     // private (for BoxComponent)
42415     getPositionEl : function(){
42416         return this.wrap;
42417     },
42418
42419     // private
42420     initEvents : function(){
42421         this.originalValue = this.getValue();
42422     },
42423
42424     /**
42425      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42426      * @method
42427      */
42428     markInvalid : Roo.emptyFn,
42429     /**
42430      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42431      * @method
42432      */
42433     clearInvalid : Roo.emptyFn,
42434
42435     setValue : function(v){
42436         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42437         this.editorcore.pushValue();
42438     },
42439
42440      
42441     // private
42442     deferFocus : function(){
42443         this.focus.defer(10, this);
42444     },
42445
42446     // doc'ed in Field
42447     focus : function(){
42448         this.editorcore.focus();
42449         
42450     },
42451       
42452
42453     // private
42454     onDestroy : function(){
42455         
42456         
42457         
42458         if(this.rendered){
42459             
42460             for (var i =0; i < this.toolbars.length;i++) {
42461                 // fixme - ask toolbars for heights?
42462                 this.toolbars[i].onDestroy();
42463             }
42464             
42465             this.wrap.dom.innerHTML = '';
42466             this.wrap.remove();
42467         }
42468     },
42469
42470     // private
42471     onFirstFocus : function(){
42472         //Roo.log("onFirstFocus");
42473         this.editorcore.onFirstFocus();
42474          for (var i =0; i < this.toolbars.length;i++) {
42475             this.toolbars[i].onFirstFocus();
42476         }
42477         
42478     },
42479     
42480     // private
42481     syncValue : function()
42482     {
42483         this.editorcore.syncValue();
42484     }
42485      
42486     
42487     // hide stuff that is not compatible
42488     /**
42489      * @event blur
42490      * @hide
42491      */
42492     /**
42493      * @event change
42494      * @hide
42495      */
42496     /**
42497      * @event focus
42498      * @hide
42499      */
42500     /**
42501      * @event specialkey
42502      * @hide
42503      */
42504     /**
42505      * @cfg {String} fieldClass @hide
42506      */
42507     /**
42508      * @cfg {String} focusClass @hide
42509      */
42510     /**
42511      * @cfg {String} autoCreate @hide
42512      */
42513     /**
42514      * @cfg {String} inputType @hide
42515      */
42516     /**
42517      * @cfg {String} invalidClass @hide
42518      */
42519     /**
42520      * @cfg {String} invalidText @hide
42521      */
42522     /**
42523      * @cfg {String} msgFx @hide
42524      */
42525     /**
42526      * @cfg {String} validateOnBlur @hide
42527      */
42528 });
42529  
42530     // <script type="text/javascript">
42531 /*
42532  * Based on
42533  * Ext JS Library 1.1.1
42534  * Copyright(c) 2006-2007, Ext JS, LLC.
42535  *  
42536  
42537  */
42538
42539 /**
42540  * @class Roo.form.HtmlEditorToolbar1
42541  * Basic Toolbar
42542  * 
42543  * Usage:
42544  *
42545  new Roo.form.HtmlEditor({
42546     ....
42547     toolbars : [
42548         new Roo.form.HtmlEditorToolbar1({
42549             disable : { fonts: 1 , format: 1, ..., ... , ...],
42550             btns : [ .... ]
42551         })
42552     }
42553      
42554  * 
42555  * @cfg {Object} disable List of elements to disable..
42556  * @cfg {Array} btns List of additional buttons.
42557  * 
42558  * 
42559  * NEEDS Extra CSS? 
42560  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42561  */
42562  
42563 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42564 {
42565     
42566     Roo.apply(this, config);
42567     
42568     // default disabled, based on 'good practice'..
42569     this.disable = this.disable || {};
42570     Roo.applyIf(this.disable, {
42571         fontSize : true,
42572         colors : true,
42573         specialElements : true
42574     });
42575     
42576     
42577     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42578     // dont call parent... till later.
42579 }
42580
42581 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42582     
42583     tb: false,
42584     
42585     rendered: false,
42586     
42587     editor : false,
42588     editorcore : false,
42589     /**
42590      * @cfg {Object} disable  List of toolbar elements to disable
42591          
42592      */
42593     disable : false,
42594     
42595     
42596      /**
42597      * @cfg {String} createLinkText The default text for the create link prompt
42598      */
42599     createLinkText : 'Please enter the URL for the link:',
42600     /**
42601      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42602      */
42603     defaultLinkValue : 'http:/'+'/',
42604    
42605     
42606       /**
42607      * @cfg {Array} fontFamilies An array of available font families
42608      */
42609     fontFamilies : [
42610         'Arial',
42611         'Courier New',
42612         'Tahoma',
42613         'Times New Roman',
42614         'Verdana'
42615     ],
42616     
42617     specialChars : [
42618            "&#169;",
42619           "&#174;",     
42620           "&#8482;",    
42621           "&#163;" ,    
42622          // "&#8212;",    
42623           "&#8230;",    
42624           "&#247;" ,    
42625         //  "&#225;" ,     ?? a acute?
42626            "&#8364;"    , //Euro
42627        //   "&#8220;"    ,
42628         //  "&#8221;"    ,
42629         //  "&#8226;"    ,
42630           "&#176;"  //   , // degrees
42631
42632          // "&#233;"     , // e ecute
42633          // "&#250;"     , // u ecute?
42634     ],
42635     
42636     specialElements : [
42637         {
42638             text: "Insert Table",
42639             xtype: 'MenuItem',
42640             xns : Roo.Menu,
42641             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42642                 
42643         },
42644         {    
42645             text: "Insert Image",
42646             xtype: 'MenuItem',
42647             xns : Roo.Menu,
42648             ihtml : '<img src="about:blank"/>'
42649             
42650         }
42651         
42652          
42653     ],
42654     
42655     
42656     inputElements : [ 
42657             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42658             "input:submit", "input:button", "select", "textarea", "label" ],
42659     formats : [
42660         ["p"] ,  
42661         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42662         ["pre"],[ "code"], 
42663         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42664         ['div'],['span']
42665     ],
42666     
42667     cleanStyles : [
42668         "font-size"
42669     ],
42670      /**
42671      * @cfg {String} defaultFont default font to use.
42672      */
42673     defaultFont: 'tahoma',
42674    
42675     fontSelect : false,
42676     
42677     
42678     formatCombo : false,
42679     
42680     init : function(editor)
42681     {
42682         this.editor = editor;
42683         this.editorcore = editor.editorcore ? editor.editorcore : editor;
42684         var editorcore = this.editorcore;
42685         
42686         var _t = this;
42687         
42688         var fid = editorcore.frameId;
42689         var etb = this;
42690         function btn(id, toggle, handler){
42691             var xid = fid + '-'+ id ;
42692             return {
42693                 id : xid,
42694                 cmd : id,
42695                 cls : 'x-btn-icon x-edit-'+id,
42696                 enableToggle:toggle !== false,
42697                 scope: _t, // was editor...
42698                 handler:handler||_t.relayBtnCmd,
42699                 clickEvent:'mousedown',
42700                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42701                 tabIndex:-1
42702             };
42703         }
42704         
42705         
42706         
42707         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42708         this.tb = tb;
42709          // stop form submits
42710         tb.el.on('click', function(e){
42711             e.preventDefault(); // what does this do?
42712         });
42713
42714         if(!this.disable.font) { // && !Roo.isSafari){
42715             /* why no safari for fonts 
42716             editor.fontSelect = tb.el.createChild({
42717                 tag:'select',
42718                 tabIndex: -1,
42719                 cls:'x-font-select',
42720                 html: this.createFontOptions()
42721             });
42722             
42723             editor.fontSelect.on('change', function(){
42724                 var font = editor.fontSelect.dom.value;
42725                 editor.relayCmd('fontname', font);
42726                 editor.deferFocus();
42727             }, editor);
42728             
42729             tb.add(
42730                 editor.fontSelect.dom,
42731                 '-'
42732             );
42733             */
42734             
42735         };
42736         if(!this.disable.formats){
42737             this.formatCombo = new Roo.form.ComboBox({
42738                 store: new Roo.data.SimpleStore({
42739                     id : 'tag',
42740                     fields: ['tag'],
42741                     data : this.formats // from states.js
42742                 }),
42743                 blockFocus : true,
42744                 name : '',
42745                 //autoCreate : {tag: "div",  size: "20"},
42746                 displayField:'tag',
42747                 typeAhead: false,
42748                 mode: 'local',
42749                 editable : false,
42750                 triggerAction: 'all',
42751                 emptyText:'Add tag',
42752                 selectOnFocus:true,
42753                 width:135,
42754                 listeners : {
42755                     'select': function(c, r, i) {
42756                         editorcore.insertTag(r.get('tag'));
42757                         editor.focus();
42758                     }
42759                 }
42760
42761             });
42762             tb.addField(this.formatCombo);
42763             
42764         }
42765         
42766         if(!this.disable.format){
42767             tb.add(
42768                 btn('bold'),
42769                 btn('italic'),
42770                 btn('underline')
42771             );
42772         };
42773         if(!this.disable.fontSize){
42774             tb.add(
42775                 '-',
42776                 
42777                 
42778                 btn('increasefontsize', false, editorcore.adjustFont),
42779                 btn('decreasefontsize', false, editorcore.adjustFont)
42780             );
42781         };
42782         
42783         
42784         if(!this.disable.colors){
42785             tb.add(
42786                 '-', {
42787                     id:editorcore.frameId +'-forecolor',
42788                     cls:'x-btn-icon x-edit-forecolor',
42789                     clickEvent:'mousedown',
42790                     tooltip: this.buttonTips['forecolor'] || undefined,
42791                     tabIndex:-1,
42792                     menu : new Roo.menu.ColorMenu({
42793                         allowReselect: true,
42794                         focus: Roo.emptyFn,
42795                         value:'000000',
42796                         plain:true,
42797                         selectHandler: function(cp, color){
42798                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42799                             editor.deferFocus();
42800                         },
42801                         scope: editorcore,
42802                         clickEvent:'mousedown'
42803                     })
42804                 }, {
42805                     id:editorcore.frameId +'backcolor',
42806                     cls:'x-btn-icon x-edit-backcolor',
42807                     clickEvent:'mousedown',
42808                     tooltip: this.buttonTips['backcolor'] || undefined,
42809                     tabIndex:-1,
42810                     menu : new Roo.menu.ColorMenu({
42811                         focus: Roo.emptyFn,
42812                         value:'FFFFFF',
42813                         plain:true,
42814                         allowReselect: true,
42815                         selectHandler: function(cp, color){
42816                             if(Roo.isGecko){
42817                                 editorcore.execCmd('useCSS', false);
42818                                 editorcore.execCmd('hilitecolor', color);
42819                                 editorcore.execCmd('useCSS', true);
42820                                 editor.deferFocus();
42821                             }else{
42822                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42823                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42824                                 editor.deferFocus();
42825                             }
42826                         },
42827                         scope:editorcore,
42828                         clickEvent:'mousedown'
42829                     })
42830                 }
42831             );
42832         };
42833         // now add all the items...
42834         
42835
42836         if(!this.disable.alignments){
42837             tb.add(
42838                 '-',
42839                 btn('justifyleft'),
42840                 btn('justifycenter'),
42841                 btn('justifyright')
42842             );
42843         };
42844
42845         //if(!Roo.isSafari){
42846             if(!this.disable.links){
42847                 tb.add(
42848                     '-',
42849                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
42850                 );
42851             };
42852
42853             if(!this.disable.lists){
42854                 tb.add(
42855                     '-',
42856                     btn('insertorderedlist'),
42857                     btn('insertunorderedlist')
42858                 );
42859             }
42860             if(!this.disable.sourceEdit){
42861                 tb.add(
42862                     '-',
42863                     btn('sourceedit', true, function(btn){
42864                         Roo.log(this);
42865                         this.toggleSourceEdit(btn.pressed);
42866                     })
42867                 );
42868             }
42869         //}
42870         
42871         var smenu = { };
42872         // special menu.. - needs to be tidied up..
42873         if (!this.disable.special) {
42874             smenu = {
42875                 text: "&#169;",
42876                 cls: 'x-edit-none',
42877                 
42878                 menu : {
42879                     items : []
42880                 }
42881             };
42882             for (var i =0; i < this.specialChars.length; i++) {
42883                 smenu.menu.items.push({
42884                     
42885                     html: this.specialChars[i],
42886                     handler: function(a,b) {
42887                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42888                         //editor.insertAtCursor(a.html);
42889                         
42890                     },
42891                     tabIndex:-1
42892                 });
42893             }
42894             
42895             
42896             tb.add(smenu);
42897             
42898             
42899         }
42900         
42901         var cmenu = { };
42902         if (!this.disable.cleanStyles) {
42903             cmenu = {
42904                 cls: 'x-btn-icon x-btn-clear',
42905                 
42906                 menu : {
42907                     items : []
42908                 }
42909             };
42910             for (var i =0; i < this.cleanStyles.length; i++) {
42911                 cmenu.menu.items.push({
42912                     actiontype : this.cleanStyles[i],
42913                     html: 'Remove ' + this.cleanStyles[i],
42914                     handler: function(a,b) {
42915                         Roo.log(a);
42916                         Roo.log(b);
42917                         var c = Roo.get(editorcore.doc.body);
42918                         c.select('[style]').each(function(s) {
42919                             s.dom.style.removeProperty(a.actiontype);
42920                         });
42921                         
42922                     },
42923                     tabIndex:-1
42924                 });
42925             }
42926             
42927             tb.add(cmenu);
42928         }
42929          
42930         if (!this.disable.specialElements) {
42931             var semenu = {
42932                 text: "Other;",
42933                 cls: 'x-edit-none',
42934                 menu : {
42935                     items : []
42936                 }
42937             };
42938             for (var i =0; i < this.specialElements.length; i++) {
42939                 semenu.menu.items.push(
42940                     Roo.apply({ 
42941                         handler: function(a,b) {
42942                             editor.insertAtCursor(this.ihtml);
42943                         }
42944                     }, this.specialElements[i])
42945                 );
42946                     
42947             }
42948             
42949             tb.add(semenu);
42950             
42951             
42952         }
42953          
42954         
42955         if (this.btns) {
42956             for(var i =0; i< this.btns.length;i++) {
42957                 var b = Roo.factory(this.btns[i],Roo.form);
42958                 b.cls =  'x-edit-none';
42959                 b.scope = editorcore;
42960                 tb.add(b);
42961             }
42962         
42963         }
42964         
42965         
42966         
42967         // disable everything...
42968         
42969         this.tb.items.each(function(item){
42970            if(item.id != editorcore.frameId+ '-sourceedit'){
42971                 item.disable();
42972             }
42973         });
42974         this.rendered = true;
42975         
42976         // the all the btns;
42977         editor.on('editorevent', this.updateToolbar, this);
42978         // other toolbars need to implement this..
42979         //editor.on('editmodechange', this.updateToolbar, this);
42980     },
42981     
42982     
42983     relayBtnCmd : function(btn) {
42984         this.editorcore.relayCmd(btn.cmd);
42985     },
42986     // private used internally
42987     createLink : function(){
42988         Roo.log("create link?");
42989         var url = prompt(this.createLinkText, this.defaultLinkValue);
42990         if(url && url != 'http:/'+'/'){
42991             this.editorcore.relayCmd('createlink', url);
42992         }
42993     },
42994
42995     
42996     /**
42997      * Protected method that will not generally be called directly. It triggers
42998      * a toolbar update by reading the markup state of the current selection in the editor.
42999      */
43000     updateToolbar: function(){
43001
43002         if(!this.editorcore.activated){
43003             this.editor.onFirstFocus();
43004             return;
43005         }
43006
43007         var btns = this.tb.items.map, 
43008             doc = this.editorcore.doc,
43009             frameId = this.editorcore.frameId;
43010
43011         if(!this.disable.font && !Roo.isSafari){
43012             /*
43013             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43014             if(name != this.fontSelect.dom.value){
43015                 this.fontSelect.dom.value = name;
43016             }
43017             */
43018         }
43019         if(!this.disable.format){
43020             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43021             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43022             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43023         }
43024         if(!this.disable.alignments){
43025             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43026             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43027             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43028         }
43029         if(!Roo.isSafari && !this.disable.lists){
43030             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43031             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43032         }
43033         
43034         var ans = this.editorcore.getAllAncestors();
43035         if (this.formatCombo) {
43036             
43037             
43038             var store = this.formatCombo.store;
43039             this.formatCombo.setValue("");
43040             for (var i =0; i < ans.length;i++) {
43041                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43042                     // select it..
43043                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43044                     break;
43045                 }
43046             }
43047         }
43048         
43049         
43050         
43051         // hides menus... - so this cant be on a menu...
43052         Roo.menu.MenuMgr.hideAll();
43053
43054         //this.editorsyncValue();
43055     },
43056    
43057     
43058     createFontOptions : function(){
43059         var buf = [], fs = this.fontFamilies, ff, lc;
43060         
43061         
43062         
43063         for(var i = 0, len = fs.length; i< len; i++){
43064             ff = fs[i];
43065             lc = ff.toLowerCase();
43066             buf.push(
43067                 '<option value="',lc,'" style="font-family:',ff,';"',
43068                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43069                     ff,
43070                 '</option>'
43071             );
43072         }
43073         return buf.join('');
43074     },
43075     
43076     toggleSourceEdit : function(sourceEditMode){
43077         
43078         Roo.log("toolbar toogle");
43079         if(sourceEditMode === undefined){
43080             sourceEditMode = !this.sourceEditMode;
43081         }
43082         this.sourceEditMode = sourceEditMode === true;
43083         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43084         // just toggle the button?
43085         if(btn.pressed !== this.sourceEditMode){
43086             btn.toggle(this.sourceEditMode);
43087             return;
43088         }
43089         
43090         if(sourceEditMode){
43091             Roo.log("disabling buttons");
43092             this.tb.items.each(function(item){
43093                 if(item.cmd != 'sourceedit'){
43094                     item.disable();
43095                 }
43096             });
43097           
43098         }else{
43099             Roo.log("enabling buttons");
43100             if(this.editorcore.initialized){
43101                 this.tb.items.each(function(item){
43102                     item.enable();
43103                 });
43104             }
43105             
43106         }
43107         Roo.log("calling toggole on editor");
43108         // tell the editor that it's been pressed..
43109         this.editor.toggleSourceEdit(sourceEditMode);
43110        
43111     },
43112      /**
43113      * Object collection of toolbar tooltips for the buttons in the editor. The key
43114      * is the command id associated with that button and the value is a valid QuickTips object.
43115      * For example:
43116 <pre><code>
43117 {
43118     bold : {
43119         title: 'Bold (Ctrl+B)',
43120         text: 'Make the selected text bold.',
43121         cls: 'x-html-editor-tip'
43122     },
43123     italic : {
43124         title: 'Italic (Ctrl+I)',
43125         text: 'Make the selected text italic.',
43126         cls: 'x-html-editor-tip'
43127     },
43128     ...
43129 </code></pre>
43130     * @type Object
43131      */
43132     buttonTips : {
43133         bold : {
43134             title: 'Bold (Ctrl+B)',
43135             text: 'Make the selected text bold.',
43136             cls: 'x-html-editor-tip'
43137         },
43138         italic : {
43139             title: 'Italic (Ctrl+I)',
43140             text: 'Make the selected text italic.',
43141             cls: 'x-html-editor-tip'
43142         },
43143         underline : {
43144             title: 'Underline (Ctrl+U)',
43145             text: 'Underline the selected text.',
43146             cls: 'x-html-editor-tip'
43147         },
43148         increasefontsize : {
43149             title: 'Grow Text',
43150             text: 'Increase the font size.',
43151             cls: 'x-html-editor-tip'
43152         },
43153         decreasefontsize : {
43154             title: 'Shrink Text',
43155             text: 'Decrease the font size.',
43156             cls: 'x-html-editor-tip'
43157         },
43158         backcolor : {
43159             title: 'Text Highlight Color',
43160             text: 'Change the background color of the selected text.',
43161             cls: 'x-html-editor-tip'
43162         },
43163         forecolor : {
43164             title: 'Font Color',
43165             text: 'Change the color of the selected text.',
43166             cls: 'x-html-editor-tip'
43167         },
43168         justifyleft : {
43169             title: 'Align Text Left',
43170             text: 'Align text to the left.',
43171             cls: 'x-html-editor-tip'
43172         },
43173         justifycenter : {
43174             title: 'Center Text',
43175             text: 'Center text in the editor.',
43176             cls: 'x-html-editor-tip'
43177         },
43178         justifyright : {
43179             title: 'Align Text Right',
43180             text: 'Align text to the right.',
43181             cls: 'x-html-editor-tip'
43182         },
43183         insertunorderedlist : {
43184             title: 'Bullet List',
43185             text: 'Start a bulleted list.',
43186             cls: 'x-html-editor-tip'
43187         },
43188         insertorderedlist : {
43189             title: 'Numbered List',
43190             text: 'Start a numbered list.',
43191             cls: 'x-html-editor-tip'
43192         },
43193         createlink : {
43194             title: 'Hyperlink',
43195             text: 'Make the selected text a hyperlink.',
43196             cls: 'x-html-editor-tip'
43197         },
43198         sourceedit : {
43199             title: 'Source Edit',
43200             text: 'Switch to source editing mode.',
43201             cls: 'x-html-editor-tip'
43202         }
43203     },
43204     // private
43205     onDestroy : function(){
43206         if(this.rendered){
43207             
43208             this.tb.items.each(function(item){
43209                 if(item.menu){
43210                     item.menu.removeAll();
43211                     if(item.menu.el){
43212                         item.menu.el.destroy();
43213                     }
43214                 }
43215                 item.destroy();
43216             });
43217              
43218         }
43219     },
43220     onFirstFocus: function() {
43221         this.tb.items.each(function(item){
43222            item.enable();
43223         });
43224     }
43225 });
43226
43227
43228
43229
43230 // <script type="text/javascript">
43231 /*
43232  * Based on
43233  * Ext JS Library 1.1.1
43234  * Copyright(c) 2006-2007, Ext JS, LLC.
43235  *  
43236  
43237  */
43238
43239  
43240 /**
43241  * @class Roo.form.HtmlEditor.ToolbarContext
43242  * Context Toolbar
43243  * 
43244  * Usage:
43245  *
43246  new Roo.form.HtmlEditor({
43247     ....
43248     toolbars : [
43249         { xtype: 'ToolbarStandard', styles : {} }
43250         { xtype: 'ToolbarContext', disable : {} }
43251     ]
43252 })
43253
43254      
43255  * 
43256  * @config : {Object} disable List of elements to disable.. (not done yet.)
43257  * @config : {Object} styles  Map of styles available.
43258  * 
43259  */
43260
43261 Roo.form.HtmlEditor.ToolbarContext = function(config)
43262 {
43263     
43264     Roo.apply(this, config);
43265     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43266     // dont call parent... till later.
43267     this.styles = this.styles || {};
43268 }
43269
43270  
43271
43272 Roo.form.HtmlEditor.ToolbarContext.types = {
43273     'IMG' : {
43274         width : {
43275             title: "Width",
43276             width: 40
43277         },
43278         height:  {
43279             title: "Height",
43280             width: 40
43281         },
43282         align: {
43283             title: "Align",
43284             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43285             width : 80
43286             
43287         },
43288         border: {
43289             title: "Border",
43290             width: 40
43291         },
43292         alt: {
43293             title: "Alt",
43294             width: 120
43295         },
43296         src : {
43297             title: "Src",
43298             width: 220
43299         }
43300         
43301     },
43302     'A' : {
43303         name : {
43304             title: "Name",
43305             width: 50
43306         },
43307         target:  {
43308             title: "Target",
43309             width: 120
43310         },
43311         href:  {
43312             title: "Href",
43313             width: 220
43314         } // border?
43315         
43316     },
43317     'TABLE' : {
43318         rows : {
43319             title: "Rows",
43320             width: 20
43321         },
43322         cols : {
43323             title: "Cols",
43324             width: 20
43325         },
43326         width : {
43327             title: "Width",
43328             width: 40
43329         },
43330         height : {
43331             title: "Height",
43332             width: 40
43333         },
43334         border : {
43335             title: "Border",
43336             width: 20
43337         }
43338     },
43339     'TD' : {
43340         width : {
43341             title: "Width",
43342             width: 40
43343         },
43344         height : {
43345             title: "Height",
43346             width: 40
43347         },   
43348         align: {
43349             title: "Align",
43350             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43351             width: 80
43352         },
43353         valign: {
43354             title: "Valign",
43355             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43356             width: 80
43357         },
43358         colspan: {
43359             title: "Colspan",
43360             width: 20
43361             
43362         },
43363          'font-family'  : {
43364             title : "Font",
43365             style : 'fontFamily',
43366             displayField: 'display',
43367             optname : 'font-family',
43368             width: 140
43369         }
43370     },
43371     'INPUT' : {
43372         name : {
43373             title: "name",
43374             width: 120
43375         },
43376         value : {
43377             title: "Value",
43378             width: 120
43379         },
43380         width : {
43381             title: "Width",
43382             width: 40
43383         }
43384     },
43385     'LABEL' : {
43386         'for' : {
43387             title: "For",
43388             width: 120
43389         }
43390     },
43391     'TEXTAREA' : {
43392           name : {
43393             title: "name",
43394             width: 120
43395         },
43396         rows : {
43397             title: "Rows",
43398             width: 20
43399         },
43400         cols : {
43401             title: "Cols",
43402             width: 20
43403         }
43404     },
43405     'SELECT' : {
43406         name : {
43407             title: "name",
43408             width: 120
43409         },
43410         selectoptions : {
43411             title: "Options",
43412             width: 200
43413         }
43414     },
43415     
43416     // should we really allow this??
43417     // should this just be 
43418     'BODY' : {
43419         title : {
43420             title: "Title",
43421             width: 200,
43422             disabled : true
43423         }
43424     },
43425     'SPAN' : {
43426         'font-family'  : {
43427             title : "Font",
43428             style : 'fontFamily',
43429             displayField: 'display',
43430             optname : 'font-family',
43431             width: 140
43432         }
43433     },
43434     'DIV' : {
43435         'font-family'  : {
43436             title : "Font",
43437             style : 'fontFamily',
43438             displayField: 'display',
43439             optname : 'font-family',
43440             width: 140
43441         }
43442     },
43443      'P' : {
43444         'font-family'  : {
43445             title : "Font",
43446             style : 'fontFamily',
43447             displayField: 'display',
43448             optname : 'font-family',
43449             width: 140
43450         }
43451     },
43452     
43453     '*' : {
43454         // empty..
43455     }
43456
43457 };
43458
43459 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43460 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43461
43462 Roo.form.HtmlEditor.ToolbarContext.options = {
43463         'font-family'  : [ 
43464                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43465                 [ 'Courier New', 'Courier New'],
43466                 [ 'Tahoma', 'Tahoma'],
43467                 [ 'Times New Roman,serif', 'Times'],
43468                 [ 'Verdana','Verdana' ]
43469         ]
43470 };
43471
43472 // fixme - these need to be configurable..
43473  
43474
43475 Roo.form.HtmlEditor.ToolbarContext.types
43476
43477
43478 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43479     
43480     tb: false,
43481     
43482     rendered: false,
43483     
43484     editor : false,
43485     editorcore : false,
43486     /**
43487      * @cfg {Object} disable  List of toolbar elements to disable
43488          
43489      */
43490     disable : false,
43491     /**
43492      * @cfg {Object} styles List of styles 
43493      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43494      *
43495      * These must be defined in the page, so they get rendered correctly..
43496      * .headline { }
43497      * TD.underline { }
43498      * 
43499      */
43500     styles : false,
43501     
43502     options: false,
43503     
43504     toolbars : false,
43505     
43506     init : function(editor)
43507     {
43508         this.editor = editor;
43509         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43510         var editorcore = this.editorcore;
43511         
43512         var fid = editorcore.frameId;
43513         var etb = this;
43514         function btn(id, toggle, handler){
43515             var xid = fid + '-'+ id ;
43516             return {
43517                 id : xid,
43518                 cmd : id,
43519                 cls : 'x-btn-icon x-edit-'+id,
43520                 enableToggle:toggle !== false,
43521                 scope: editorcore, // was editor...
43522                 handler:handler||editorcore.relayBtnCmd,
43523                 clickEvent:'mousedown',
43524                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43525                 tabIndex:-1
43526             };
43527         }
43528         // create a new element.
43529         var wdiv = editor.wrap.createChild({
43530                 tag: 'div'
43531             }, editor.wrap.dom.firstChild.nextSibling, true);
43532         
43533         // can we do this more than once??
43534         
43535          // stop form submits
43536       
43537  
43538         // disable everything...
43539         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43540         this.toolbars = {};
43541            
43542         for (var i in  ty) {
43543           
43544             this.toolbars[i] = this.buildToolbar(ty[i],i);
43545         }
43546         this.tb = this.toolbars.BODY;
43547         this.tb.el.show();
43548         this.buildFooter();
43549         this.footer.show();
43550         editor.on('hide', function( ) { this.footer.hide() }, this);
43551         editor.on('show', function( ) { this.footer.show() }, this);
43552         
43553          
43554         this.rendered = true;
43555         
43556         // the all the btns;
43557         editor.on('editorevent', this.updateToolbar, this);
43558         // other toolbars need to implement this..
43559         //editor.on('editmodechange', this.updateToolbar, this);
43560     },
43561     
43562     
43563     
43564     /**
43565      * Protected method that will not generally be called directly. It triggers
43566      * a toolbar update by reading the markup state of the current selection in the editor.
43567      */
43568     updateToolbar: function(editor,ev,sel){
43569
43570         //Roo.log(ev);
43571         // capture mouse up - this is handy for selecting images..
43572         // perhaps should go somewhere else...
43573         if(!this.editorcore.activated){
43574              this.editor.onFirstFocus();
43575             return;
43576         }
43577         
43578         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43579         // selectNode - might want to handle IE?
43580         if (ev &&
43581             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43582             ev.target && ev.target.tagName == 'IMG') {
43583             // they have click on an image...
43584             // let's see if we can change the selection...
43585             sel = ev.target;
43586          
43587               var nodeRange = sel.ownerDocument.createRange();
43588             try {
43589                 nodeRange.selectNode(sel);
43590             } catch (e) {
43591                 nodeRange.selectNodeContents(sel);
43592             }
43593             //nodeRange.collapse(true);
43594             var s = this.editorcore.win.getSelection();
43595             s.removeAllRanges();
43596             s.addRange(nodeRange);
43597         }  
43598         
43599       
43600         var updateFooter = sel ? false : true;
43601         
43602         
43603         var ans = this.editorcore.getAllAncestors();
43604         
43605         // pick
43606         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43607         
43608         if (!sel) { 
43609             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43610             sel = sel ? sel : this.editorcore.doc.body;
43611             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43612             
43613         }
43614         // pick a menu that exists..
43615         var tn = sel.tagName.toUpperCase();
43616         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43617         
43618         tn = sel.tagName.toUpperCase();
43619         
43620         var lastSel = this.tb.selectedNode
43621         
43622         this.tb.selectedNode = sel;
43623         
43624         // if current menu does not match..
43625         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43626                 
43627             this.tb.el.hide();
43628             ///console.log("show: " + tn);
43629             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43630             this.tb.el.show();
43631             // update name
43632             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43633             
43634             
43635             // update attributes
43636             if (this.tb.fields) {
43637                 this.tb.fields.each(function(e) {
43638                     if (e.stylename) {
43639                         e.setValue(sel.style[e.stylename]);
43640                         return;
43641                     } 
43642                    e.setValue(sel.getAttribute(e.attrname));
43643                 });
43644             }
43645             
43646             var hasStyles = false;
43647             for(var i in this.styles) {
43648                 hasStyles = true;
43649                 break;
43650             }
43651             
43652             // update styles
43653             if (hasStyles) { 
43654                 var st = this.tb.fields.item(0);
43655                 
43656                 st.store.removeAll();
43657                
43658                 
43659                 var cn = sel.className.split(/\s+/);
43660                 
43661                 var avs = [];
43662                 if (this.styles['*']) {
43663                     
43664                     Roo.each(this.styles['*'], function(v) {
43665                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43666                     });
43667                 }
43668                 if (this.styles[tn]) { 
43669                     Roo.each(this.styles[tn], function(v) {
43670                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43671                     });
43672                 }
43673                 
43674                 st.store.loadData(avs);
43675                 st.collapse();
43676                 st.setValue(cn);
43677             }
43678             // flag our selected Node.
43679             this.tb.selectedNode = sel;
43680            
43681            
43682             Roo.menu.MenuMgr.hideAll();
43683
43684         }
43685         
43686         if (!updateFooter) {
43687             //this.footDisp.dom.innerHTML = ''; 
43688             return;
43689         }
43690         // update the footer
43691         //
43692         var html = '';
43693         
43694         this.footerEls = ans.reverse();
43695         Roo.each(this.footerEls, function(a,i) {
43696             if (!a) { return; }
43697             html += html.length ? ' &gt; '  :  '';
43698             
43699             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43700             
43701         });
43702        
43703         // 
43704         var sz = this.footDisp.up('td').getSize();
43705         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43706         this.footDisp.dom.style.marginLeft = '5px';
43707         
43708         this.footDisp.dom.style.overflow = 'hidden';
43709         
43710         this.footDisp.dom.innerHTML = html;
43711             
43712         //this.editorsyncValue();
43713     },
43714      
43715     
43716    
43717        
43718     // private
43719     onDestroy : function(){
43720         if(this.rendered){
43721             
43722             this.tb.items.each(function(item){
43723                 if(item.menu){
43724                     item.menu.removeAll();
43725                     if(item.menu.el){
43726                         item.menu.el.destroy();
43727                     }
43728                 }
43729                 item.destroy();
43730             });
43731              
43732         }
43733     },
43734     onFirstFocus: function() {
43735         // need to do this for all the toolbars..
43736         this.tb.items.each(function(item){
43737            item.enable();
43738         });
43739     },
43740     buildToolbar: function(tlist, nm)
43741     {
43742         var editor = this.editor;
43743         var editorcore = this.editorcore;
43744          // create a new element.
43745         var wdiv = editor.wrap.createChild({
43746                 tag: 'div'
43747             }, editor.wrap.dom.firstChild.nextSibling, true);
43748         
43749        
43750         var tb = new Roo.Toolbar(wdiv);
43751         // add the name..
43752         
43753         tb.add(nm+ ":&nbsp;");
43754         
43755         var styles = [];
43756         for(var i in this.styles) {
43757             styles.push(i);
43758         }
43759         
43760         // styles...
43761         if (styles && styles.length) {
43762             
43763             // this needs a multi-select checkbox...
43764             tb.addField( new Roo.form.ComboBox({
43765                 store: new Roo.data.SimpleStore({
43766                     id : 'val',
43767                     fields: ['val', 'selected'],
43768                     data : [] 
43769                 }),
43770                 name : '-roo-edit-className',
43771                 attrname : 'className',
43772                 displayField: 'val',
43773                 typeAhead: false,
43774                 mode: 'local',
43775                 editable : false,
43776                 triggerAction: 'all',
43777                 emptyText:'Select Style',
43778                 selectOnFocus:true,
43779                 width: 130,
43780                 listeners : {
43781                     'select': function(c, r, i) {
43782                         // initial support only for on class per el..
43783                         tb.selectedNode.className =  r ? r.get('val') : '';
43784                         editorcore.syncValue();
43785                     }
43786                 }
43787     
43788             }));
43789         }
43790         
43791         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43792         var tbops = tbc.options;
43793         
43794         for (var i in tlist) {
43795             
43796             var item = tlist[i];
43797             tb.add(item.title + ":&nbsp;");
43798             
43799             
43800             //optname == used so you can configure the options available..
43801             var opts = item.opts ? item.opts : false;
43802             if (item.optname) {
43803                 opts = tbops[item.optname];
43804            
43805             }
43806             
43807             if (opts) {
43808                 // opts == pulldown..
43809                 tb.addField( new Roo.form.ComboBox({
43810                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43811                         id : 'val',
43812                         fields: ['val', 'display'],
43813                         data : opts  
43814                     }),
43815                     name : '-roo-edit-' + i,
43816                     attrname : i,
43817                     stylename : item.style ? item.style : false,
43818                     displayField: item.displayField ? item.displayField : 'val',
43819                     valueField :  'val',
43820                     typeAhead: false,
43821                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43822                     editable : false,
43823                     triggerAction: 'all',
43824                     emptyText:'Select',
43825                     selectOnFocus:true,
43826                     width: item.width ? item.width  : 130,
43827                     listeners : {
43828                         'select': function(c, r, i) {
43829                             if (c.stylename) {
43830                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43831                                 return;
43832                             }
43833                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43834                         }
43835                     }
43836
43837                 }));
43838                 continue;
43839                     
43840                  
43841                 
43842                 tb.addField( new Roo.form.TextField({
43843                     name: i,
43844                     width: 100,
43845                     //allowBlank:false,
43846                     value: ''
43847                 }));
43848                 continue;
43849             }
43850             tb.addField( new Roo.form.TextField({
43851                 name: '-roo-edit-' + i,
43852                 attrname : i,
43853                 
43854                 width: item.width,
43855                 //allowBlank:true,
43856                 value: '',
43857                 listeners: {
43858                     'change' : function(f, nv, ov) {
43859                         tb.selectedNode.setAttribute(f.attrname, nv);
43860                     }
43861                 }
43862             }));
43863              
43864         }
43865         tb.addFill();
43866         var _this = this;
43867         tb.addButton( {
43868             text: 'Remove Tag',
43869     
43870             listeners : {
43871                 click : function ()
43872                 {
43873                     // remove
43874                     // undo does not work.
43875                      
43876                     var sn = tb.selectedNode;
43877                     
43878                     var pn = sn.parentNode;
43879                     
43880                     var stn =  sn.childNodes[0];
43881                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43882                     while (sn.childNodes.length) {
43883                         var node = sn.childNodes[0];
43884                         sn.removeChild(node);
43885                         //Roo.log(node);
43886                         pn.insertBefore(node, sn);
43887                         
43888                     }
43889                     pn.removeChild(sn);
43890                     var range = editorcore.createRange();
43891         
43892                     range.setStart(stn,0);
43893                     range.setEnd(en,0); //????
43894                     //range.selectNode(sel);
43895                     
43896                     
43897                     var selection = editorcore.getSelection();
43898                     selection.removeAllRanges();
43899                     selection.addRange(range);
43900                     
43901                     
43902                     
43903                     //_this.updateToolbar(null, null, pn);
43904                     _this.updateToolbar(null, null, null);
43905                     _this.footDisp.dom.innerHTML = ''; 
43906                 }
43907             }
43908             
43909                     
43910                 
43911             
43912         });
43913         
43914         
43915         tb.el.on('click', function(e){
43916             e.preventDefault(); // what does this do?
43917         });
43918         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43919         tb.el.hide();
43920         tb.name = nm;
43921         // dont need to disable them... as they will get hidden
43922         return tb;
43923          
43924         
43925     },
43926     buildFooter : function()
43927     {
43928         
43929         var fel = this.editor.wrap.createChild();
43930         this.footer = new Roo.Toolbar(fel);
43931         // toolbar has scrolly on left / right?
43932         var footDisp= new Roo.Toolbar.Fill();
43933         var _t = this;
43934         this.footer.add(
43935             {
43936                 text : '&lt;',
43937                 xtype: 'Button',
43938                 handler : function() {
43939                     _t.footDisp.scrollTo('left',0,true)
43940                 }
43941             }
43942         );
43943         this.footer.add( footDisp );
43944         this.footer.add( 
43945             {
43946                 text : '&gt;',
43947                 xtype: 'Button',
43948                 handler : function() {
43949                     // no animation..
43950                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43951                 }
43952             }
43953         );
43954         var fel = Roo.get(footDisp.el);
43955         fel.addClass('x-editor-context');
43956         this.footDispWrap = fel; 
43957         this.footDispWrap.overflow  = 'hidden';
43958         
43959         this.footDisp = fel.createChild();
43960         this.footDispWrap.on('click', this.onContextClick, this)
43961         
43962         
43963     },
43964     onContextClick : function (ev,dom)
43965     {
43966         ev.preventDefault();
43967         var  cn = dom.className;
43968         //Roo.log(cn);
43969         if (!cn.match(/x-ed-loc-/)) {
43970             return;
43971         }
43972         var n = cn.split('-').pop();
43973         var ans = this.footerEls;
43974         var sel = ans[n];
43975         
43976          // pick
43977         var range = this.editorcore.createRange();
43978         
43979         range.selectNodeContents(sel);
43980         //range.selectNode(sel);
43981         
43982         
43983         var selection = this.editorcore.getSelection();
43984         selection.removeAllRanges();
43985         selection.addRange(range);
43986         
43987         
43988         
43989         this.updateToolbar(null, null, sel);
43990         
43991         
43992     }
43993     
43994     
43995     
43996     
43997     
43998 });
43999
44000
44001
44002
44003
44004 /*
44005  * Based on:
44006  * Ext JS Library 1.1.1
44007  * Copyright(c) 2006-2007, Ext JS, LLC.
44008  *
44009  * Originally Released Under LGPL - original licence link has changed is not relivant.
44010  *
44011  * Fork - LGPL
44012  * <script type="text/javascript">
44013  */
44014  
44015 /**
44016  * @class Roo.form.BasicForm
44017  * @extends Roo.util.Observable
44018  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44019  * @constructor
44020  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44021  * @param {Object} config Configuration options
44022  */
44023 Roo.form.BasicForm = function(el, config){
44024     this.allItems = [];
44025     this.childForms = [];
44026     Roo.apply(this, config);
44027     /*
44028      * The Roo.form.Field items in this form.
44029      * @type MixedCollection
44030      */
44031      
44032      
44033     this.items = new Roo.util.MixedCollection(false, function(o){
44034         return o.id || (o.id = Roo.id());
44035     });
44036     this.addEvents({
44037         /**
44038          * @event beforeaction
44039          * Fires before any action is performed. Return false to cancel the action.
44040          * @param {Form} this
44041          * @param {Action} action The action to be performed
44042          */
44043         beforeaction: true,
44044         /**
44045          * @event actionfailed
44046          * Fires when an action fails.
44047          * @param {Form} this
44048          * @param {Action} action The action that failed
44049          */
44050         actionfailed : true,
44051         /**
44052          * @event actioncomplete
44053          * Fires when an action is completed.
44054          * @param {Form} this
44055          * @param {Action} action The action that completed
44056          */
44057         actioncomplete : true
44058     });
44059     if(el){
44060         this.initEl(el);
44061     }
44062     Roo.form.BasicForm.superclass.constructor.call(this);
44063 };
44064
44065 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44066     /**
44067      * @cfg {String} method
44068      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44069      */
44070     /**
44071      * @cfg {DataReader} reader
44072      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44073      * This is optional as there is built-in support for processing JSON.
44074      */
44075     /**
44076      * @cfg {DataReader} errorReader
44077      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44078      * This is completely optional as there is built-in support for processing JSON.
44079      */
44080     /**
44081      * @cfg {String} url
44082      * The URL to use for form actions if one isn't supplied in the action options.
44083      */
44084     /**
44085      * @cfg {Boolean} fileUpload
44086      * Set to true if this form is a file upload.
44087      */
44088      
44089     /**
44090      * @cfg {Object} baseParams
44091      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44092      */
44093      /**
44094      
44095     /**
44096      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44097      */
44098     timeout: 30,
44099
44100     // private
44101     activeAction : null,
44102
44103     /**
44104      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44105      * or setValues() data instead of when the form was first created.
44106      */
44107     trackResetOnLoad : false,
44108     
44109     
44110     /**
44111      * childForms - used for multi-tab forms
44112      * @type {Array}
44113      */
44114     childForms : false,
44115     
44116     /**
44117      * allItems - full list of fields.
44118      * @type {Array}
44119      */
44120     allItems : false,
44121     
44122     /**
44123      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44124      * element by passing it or its id or mask the form itself by passing in true.
44125      * @type Mixed
44126      */
44127     waitMsgTarget : false,
44128
44129     // private
44130     initEl : function(el){
44131         this.el = Roo.get(el);
44132         this.id = this.el.id || Roo.id();
44133         this.el.on('submit', this.onSubmit, this);
44134         this.el.addClass('x-form');
44135     },
44136
44137     // private
44138     onSubmit : function(e){
44139         e.stopEvent();
44140     },
44141
44142     /**
44143      * Returns true if client-side validation on the form is successful.
44144      * @return Boolean
44145      */
44146     isValid : function(){
44147         var valid = true;
44148         this.items.each(function(f){
44149            if(!f.validate()){
44150                valid = false;
44151            }
44152         });
44153         return valid;
44154     },
44155
44156     /**
44157      * Returns true if any fields in this form have changed since their original load.
44158      * @return Boolean
44159      */
44160     isDirty : function(){
44161         var dirty = false;
44162         this.items.each(function(f){
44163            if(f.isDirty()){
44164                dirty = true;
44165                return false;
44166            }
44167         });
44168         return dirty;
44169     },
44170
44171     /**
44172      * Performs a predefined action (submit or load) or custom actions you define on this form.
44173      * @param {String} actionName The name of the action type
44174      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44175      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44176      * accept other config options):
44177      * <pre>
44178 Property          Type             Description
44179 ----------------  ---------------  ----------------------------------------------------------------------------------
44180 url               String           The url for the action (defaults to the form's url)
44181 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44182 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44183 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44184                                    validate the form on the client (defaults to false)
44185      * </pre>
44186      * @return {BasicForm} this
44187      */
44188     doAction : function(action, options){
44189         if(typeof action == 'string'){
44190             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44191         }
44192         if(this.fireEvent('beforeaction', this, action) !== false){
44193             this.beforeAction(action);
44194             action.run.defer(100, action);
44195         }
44196         return this;
44197     },
44198
44199     /**
44200      * Shortcut to do a submit action.
44201      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44202      * @return {BasicForm} this
44203      */
44204     submit : function(options){
44205         this.doAction('submit', options);
44206         return this;
44207     },
44208
44209     /**
44210      * Shortcut to do a load action.
44211      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44212      * @return {BasicForm} this
44213      */
44214     load : function(options){
44215         this.doAction('load', options);
44216         return this;
44217     },
44218
44219     /**
44220      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44221      * @param {Record} record The record to edit
44222      * @return {BasicForm} this
44223      */
44224     updateRecord : function(record){
44225         record.beginEdit();
44226         var fs = record.fields;
44227         fs.each(function(f){
44228             var field = this.findField(f.name);
44229             if(field){
44230                 record.set(f.name, field.getValue());
44231             }
44232         }, this);
44233         record.endEdit();
44234         return this;
44235     },
44236
44237     /**
44238      * Loads an Roo.data.Record into this form.
44239      * @param {Record} record The record to load
44240      * @return {BasicForm} this
44241      */
44242     loadRecord : function(record){
44243         this.setValues(record.data);
44244         return this;
44245     },
44246
44247     // private
44248     beforeAction : function(action){
44249         var o = action.options;
44250         
44251        
44252         if(this.waitMsgTarget === true){
44253             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44254         }else if(this.waitMsgTarget){
44255             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44256             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44257         }else {
44258             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44259         }
44260          
44261     },
44262
44263     // private
44264     afterAction : function(action, success){
44265         this.activeAction = null;
44266         var o = action.options;
44267         
44268         if(this.waitMsgTarget === true){
44269             this.el.unmask();
44270         }else if(this.waitMsgTarget){
44271             this.waitMsgTarget.unmask();
44272         }else{
44273             Roo.MessageBox.updateProgress(1);
44274             Roo.MessageBox.hide();
44275         }
44276          
44277         if(success){
44278             if(o.reset){
44279                 this.reset();
44280             }
44281             Roo.callback(o.success, o.scope, [this, action]);
44282             this.fireEvent('actioncomplete', this, action);
44283             
44284         }else{
44285             
44286             // failure condition..
44287             // we have a scenario where updates need confirming.
44288             // eg. if a locking scenario exists..
44289             // we look for { errors : { needs_confirm : true }} in the response.
44290             if (
44291                 (typeof(action.result) != 'undefined')  &&
44292                 (typeof(action.result.errors) != 'undefined')  &&
44293                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44294            ){
44295                 var _t = this;
44296                 Roo.MessageBox.confirm(
44297                     "Change requires confirmation",
44298                     action.result.errorMsg,
44299                     function(r) {
44300                         if (r != 'yes') {
44301                             return;
44302                         }
44303                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44304                     }
44305                     
44306                 );
44307                 
44308                 
44309                 
44310                 return;
44311             }
44312             
44313             Roo.callback(o.failure, o.scope, [this, action]);
44314             // show an error message if no failed handler is set..
44315             if (!this.hasListener('actionfailed')) {
44316                 Roo.MessageBox.alert("Error",
44317                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44318                         action.result.errorMsg :
44319                         "Saving Failed, please check your entries or try again"
44320                 );
44321             }
44322             
44323             this.fireEvent('actionfailed', this, action);
44324         }
44325         
44326     },
44327
44328     /**
44329      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44330      * @param {String} id The value to search for
44331      * @return Field
44332      */
44333     findField : function(id){
44334         var field = this.items.get(id);
44335         if(!field){
44336             this.items.each(function(f){
44337                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44338                     field = f;
44339                     return false;
44340                 }
44341             });
44342         }
44343         return field || null;
44344     },
44345
44346     /**
44347      * Add a secondary form to this one, 
44348      * Used to provide tabbed forms. One form is primary, with hidden values 
44349      * which mirror the elements from the other forms.
44350      * 
44351      * @param {Roo.form.Form} form to add.
44352      * 
44353      */
44354     addForm : function(form)
44355     {
44356        
44357         if (this.childForms.indexOf(form) > -1) {
44358             // already added..
44359             return;
44360         }
44361         this.childForms.push(form);
44362         var n = '';
44363         Roo.each(form.allItems, function (fe) {
44364             
44365             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44366             if (this.findField(n)) { // already added..
44367                 return;
44368             }
44369             var add = new Roo.form.Hidden({
44370                 name : n
44371             });
44372             add.render(this.el);
44373             
44374             this.add( add );
44375         }, this);
44376         
44377     },
44378     /**
44379      * Mark fields in this form invalid in bulk.
44380      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44381      * @return {BasicForm} this
44382      */
44383     markInvalid : function(errors){
44384         if(errors instanceof Array){
44385             for(var i = 0, len = errors.length; i < len; i++){
44386                 var fieldError = errors[i];
44387                 var f = this.findField(fieldError.id);
44388                 if(f){
44389                     f.markInvalid(fieldError.msg);
44390                 }
44391             }
44392         }else{
44393             var field, id;
44394             for(id in errors){
44395                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44396                     field.markInvalid(errors[id]);
44397                 }
44398             }
44399         }
44400         Roo.each(this.childForms || [], function (f) {
44401             f.markInvalid(errors);
44402         });
44403         
44404         return this;
44405     },
44406
44407     /**
44408      * Set values for fields in this form in bulk.
44409      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44410      * @return {BasicForm} this
44411      */
44412     setValues : function(values){
44413         if(values instanceof Array){ // array of objects
44414             for(var i = 0, len = values.length; i < len; i++){
44415                 var v = values[i];
44416                 var f = this.findField(v.id);
44417                 if(f){
44418                     f.setValue(v.value);
44419                     if(this.trackResetOnLoad){
44420                         f.originalValue = f.getValue();
44421                     }
44422                 }
44423             }
44424         }else{ // object hash
44425             var field, id;
44426             for(id in values){
44427                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44428                     
44429                     if (field.setFromData && 
44430                         field.valueField && 
44431                         field.displayField &&
44432                         // combos' with local stores can 
44433                         // be queried via setValue()
44434                         // to set their value..
44435                         (field.store && !field.store.isLocal)
44436                         ) {
44437                         // it's a combo
44438                         var sd = { };
44439                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44440                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44441                         field.setFromData(sd);
44442                         
44443                     } else {
44444                         field.setValue(values[id]);
44445                     }
44446                     
44447                     
44448                     if(this.trackResetOnLoad){
44449                         field.originalValue = field.getValue();
44450                     }
44451                 }
44452             }
44453         }
44454          
44455         Roo.each(this.childForms || [], function (f) {
44456             f.setValues(values);
44457         });
44458                 
44459         return this;
44460     },
44461
44462     /**
44463      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44464      * they are returned as an array.
44465      * @param {Boolean} asString
44466      * @return {Object}
44467      */
44468     getValues : function(asString){
44469         if (this.childForms) {
44470             // copy values from the child forms
44471             Roo.each(this.childForms, function (f) {
44472                 this.setValues(f.getValues());
44473             }, this);
44474         }
44475         
44476         
44477         
44478         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44479         if(asString === true){
44480             return fs;
44481         }
44482         return Roo.urlDecode(fs);
44483     },
44484     
44485     /**
44486      * Returns the fields in this form as an object with key/value pairs. 
44487      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44488      * @return {Object}
44489      */
44490     getFieldValues : function(with_hidden)
44491     {
44492         if (this.childForms) {
44493             // copy values from the child forms
44494             // should this call getFieldValues - probably not as we do not currently copy
44495             // hidden fields when we generate..
44496             Roo.each(this.childForms, function (f) {
44497                 this.setValues(f.getValues());
44498             }, this);
44499         }
44500         
44501         var ret = {};
44502         this.items.each(function(f){
44503             if (!f.getName()) {
44504                 return;
44505             }
44506             var v = f.getValue();
44507             if (f.inputType =='radio') {
44508                 if (typeof(ret[f.getName()]) == 'undefined') {
44509                     ret[f.getName()] = ''; // empty..
44510                 }
44511                 
44512                 if (!f.el.dom.checked) {
44513                     return;
44514                     
44515                 }
44516                 v = f.el.dom.value;
44517                 
44518             }
44519             
44520             // not sure if this supported any more..
44521             if ((typeof(v) == 'object') && f.getRawValue) {
44522                 v = f.getRawValue() ; // dates..
44523             }
44524             // combo boxes where name != hiddenName...
44525             if (f.name != f.getName()) {
44526                 ret[f.name] = f.getRawValue();
44527             }
44528             ret[f.getName()] = v;
44529         });
44530         
44531         return ret;
44532     },
44533
44534     /**
44535      * Clears all invalid messages in this form.
44536      * @return {BasicForm} this
44537      */
44538     clearInvalid : function(){
44539         this.items.each(function(f){
44540            f.clearInvalid();
44541         });
44542         
44543         Roo.each(this.childForms || [], function (f) {
44544             f.clearInvalid();
44545         });
44546         
44547         
44548         return this;
44549     },
44550
44551     /**
44552      * Resets this form.
44553      * @return {BasicForm} this
44554      */
44555     reset : function(){
44556         this.items.each(function(f){
44557             f.reset();
44558         });
44559         
44560         Roo.each(this.childForms || [], function (f) {
44561             f.reset();
44562         });
44563        
44564         
44565         return this;
44566     },
44567
44568     /**
44569      * Add Roo.form components to this form.
44570      * @param {Field} field1
44571      * @param {Field} field2 (optional)
44572      * @param {Field} etc (optional)
44573      * @return {BasicForm} this
44574      */
44575     add : function(){
44576         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44577         return this;
44578     },
44579
44580
44581     /**
44582      * Removes a field from the items collection (does NOT remove its markup).
44583      * @param {Field} field
44584      * @return {BasicForm} this
44585      */
44586     remove : function(field){
44587         this.items.remove(field);
44588         return this;
44589     },
44590
44591     /**
44592      * Looks at the fields in this form, checks them for an id attribute,
44593      * and calls applyTo on the existing dom element with that id.
44594      * @return {BasicForm} this
44595      */
44596     render : function(){
44597         this.items.each(function(f){
44598             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44599                 f.applyTo(f.id);
44600             }
44601         });
44602         return this;
44603     },
44604
44605     /**
44606      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44607      * @param {Object} values
44608      * @return {BasicForm} this
44609      */
44610     applyToFields : function(o){
44611         this.items.each(function(f){
44612            Roo.apply(f, o);
44613         });
44614         return this;
44615     },
44616
44617     /**
44618      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44619      * @param {Object} values
44620      * @return {BasicForm} this
44621      */
44622     applyIfToFields : function(o){
44623         this.items.each(function(f){
44624            Roo.applyIf(f, o);
44625         });
44626         return this;
44627     }
44628 });
44629
44630 // back compat
44631 Roo.BasicForm = Roo.form.BasicForm;/*
44632  * Based on:
44633  * Ext JS Library 1.1.1
44634  * Copyright(c) 2006-2007, Ext JS, LLC.
44635  *
44636  * Originally Released Under LGPL - original licence link has changed is not relivant.
44637  *
44638  * Fork - LGPL
44639  * <script type="text/javascript">
44640  */
44641
44642 /**
44643  * @class Roo.form.Form
44644  * @extends Roo.form.BasicForm
44645  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44646  * @constructor
44647  * @param {Object} config Configuration options
44648  */
44649 Roo.form.Form = function(config){
44650     var xitems =  [];
44651     if (config.items) {
44652         xitems = config.items;
44653         delete config.items;
44654     }
44655    
44656     
44657     Roo.form.Form.superclass.constructor.call(this, null, config);
44658     this.url = this.url || this.action;
44659     if(!this.root){
44660         this.root = new Roo.form.Layout(Roo.applyIf({
44661             id: Roo.id()
44662         }, config));
44663     }
44664     this.active = this.root;
44665     /**
44666      * Array of all the buttons that have been added to this form via {@link addButton}
44667      * @type Array
44668      */
44669     this.buttons = [];
44670     this.allItems = [];
44671     this.addEvents({
44672         /**
44673          * @event clientvalidation
44674          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44675          * @param {Form} this
44676          * @param {Boolean} valid true if the form has passed client-side validation
44677          */
44678         clientvalidation: true,
44679         /**
44680          * @event rendered
44681          * Fires when the form is rendered
44682          * @param {Roo.form.Form} form
44683          */
44684         rendered : true
44685     });
44686     
44687     if (this.progressUrl) {
44688             // push a hidden field onto the list of fields..
44689             this.addxtype( {
44690                     xns: Roo.form, 
44691                     xtype : 'Hidden', 
44692                     name : 'UPLOAD_IDENTIFIER' 
44693             });
44694         }
44695         
44696     
44697     Roo.each(xitems, this.addxtype, this);
44698     
44699     
44700     
44701 };
44702
44703 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44704     /**
44705      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44706      */
44707     /**
44708      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44709      */
44710     /**
44711      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44712      */
44713     buttonAlign:'center',
44714
44715     /**
44716      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44717      */
44718     minButtonWidth:75,
44719
44720     /**
44721      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44722      * This property cascades to child containers if not set.
44723      */
44724     labelAlign:'left',
44725
44726     /**
44727      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44728      * fires a looping event with that state. This is required to bind buttons to the valid
44729      * state using the config value formBind:true on the button.
44730      */
44731     monitorValid : false,
44732
44733     /**
44734      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44735      */
44736     monitorPoll : 200,
44737     
44738     /**
44739      * @cfg {String} progressUrl - Url to return progress data 
44740      */
44741     
44742     progressUrl : false,
44743   
44744     /**
44745      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44746      * fields are added and the column is closed. If no fields are passed the column remains open
44747      * until end() is called.
44748      * @param {Object} config The config to pass to the column
44749      * @param {Field} field1 (optional)
44750      * @param {Field} field2 (optional)
44751      * @param {Field} etc (optional)
44752      * @return Column The column container object
44753      */
44754     column : function(c){
44755         var col = new Roo.form.Column(c);
44756         this.start(col);
44757         if(arguments.length > 1){ // duplicate code required because of Opera
44758             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44759             this.end();
44760         }
44761         return col;
44762     },
44763
44764     /**
44765      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44766      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44767      * until end() is called.
44768      * @param {Object} config The config to pass to the fieldset
44769      * @param {Field} field1 (optional)
44770      * @param {Field} field2 (optional)
44771      * @param {Field} etc (optional)
44772      * @return FieldSet The fieldset container object
44773      */
44774     fieldset : function(c){
44775         var fs = new Roo.form.FieldSet(c);
44776         this.start(fs);
44777         if(arguments.length > 1){ // duplicate code required because of Opera
44778             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44779             this.end();
44780         }
44781         return fs;
44782     },
44783
44784     /**
44785      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44786      * fields are added and the container is closed. If no fields are passed the container remains open
44787      * until end() is called.
44788      * @param {Object} config The config to pass to the Layout
44789      * @param {Field} field1 (optional)
44790      * @param {Field} field2 (optional)
44791      * @param {Field} etc (optional)
44792      * @return Layout The container object
44793      */
44794     container : function(c){
44795         var l = new Roo.form.Layout(c);
44796         this.start(l);
44797         if(arguments.length > 1){ // duplicate code required because of Opera
44798             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44799             this.end();
44800         }
44801         return l;
44802     },
44803
44804     /**
44805      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44806      * @param {Object} container A Roo.form.Layout or subclass of Layout
44807      * @return {Form} this
44808      */
44809     start : function(c){
44810         // cascade label info
44811         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44812         this.active.stack.push(c);
44813         c.ownerCt = this.active;
44814         this.active = c;
44815         return this;
44816     },
44817
44818     /**
44819      * Closes the current open container
44820      * @return {Form} this
44821      */
44822     end : function(){
44823         if(this.active == this.root){
44824             return this;
44825         }
44826         this.active = this.active.ownerCt;
44827         return this;
44828     },
44829
44830     /**
44831      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44832      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44833      * as the label of the field.
44834      * @param {Field} field1
44835      * @param {Field} field2 (optional)
44836      * @param {Field} etc. (optional)
44837      * @return {Form} this
44838      */
44839     add : function(){
44840         this.active.stack.push.apply(this.active.stack, arguments);
44841         this.allItems.push.apply(this.allItems,arguments);
44842         var r = [];
44843         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44844             if(a[i].isFormField){
44845                 r.push(a[i]);
44846             }
44847         }
44848         if(r.length > 0){
44849             Roo.form.Form.superclass.add.apply(this, r);
44850         }
44851         return this;
44852     },
44853     
44854
44855     
44856     
44857     
44858      /**
44859      * Find any element that has been added to a form, using it's ID or name
44860      * This can include framesets, columns etc. along with regular fields..
44861      * @param {String} id - id or name to find.
44862      
44863      * @return {Element} e - or false if nothing found.
44864      */
44865     findbyId : function(id)
44866     {
44867         var ret = false;
44868         if (!id) {
44869             return ret;
44870         }
44871         Roo.each(this.allItems, function(f){
44872             if (f.id == id || f.name == id ){
44873                 ret = f;
44874                 return false;
44875             }
44876         });
44877         return ret;
44878     },
44879
44880     
44881     
44882     /**
44883      * Render this form into the passed container. This should only be called once!
44884      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44885      * @return {Form} this
44886      */
44887     render : function(ct)
44888     {
44889         
44890         
44891         
44892         ct = Roo.get(ct);
44893         var o = this.autoCreate || {
44894             tag: 'form',
44895             method : this.method || 'POST',
44896             id : this.id || Roo.id()
44897         };
44898         this.initEl(ct.createChild(o));
44899
44900         this.root.render(this.el);
44901         
44902        
44903              
44904         this.items.each(function(f){
44905             f.render('x-form-el-'+f.id);
44906         });
44907
44908         if(this.buttons.length > 0){
44909             // tables are required to maintain order and for correct IE layout
44910             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44911                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44912                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44913             }}, null, true);
44914             var tr = tb.getElementsByTagName('tr')[0];
44915             for(var i = 0, len = this.buttons.length; i < len; i++) {
44916                 var b = this.buttons[i];
44917                 var td = document.createElement('td');
44918                 td.className = 'x-form-btn-td';
44919                 b.render(tr.appendChild(td));
44920             }
44921         }
44922         if(this.monitorValid){ // initialize after render
44923             this.startMonitoring();
44924         }
44925         this.fireEvent('rendered', this);
44926         return this;
44927     },
44928
44929     /**
44930      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44931      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44932      * object or a valid Roo.DomHelper element config
44933      * @param {Function} handler The function called when the button is clicked
44934      * @param {Object} scope (optional) The scope of the handler function
44935      * @return {Roo.Button}
44936      */
44937     addButton : function(config, handler, scope){
44938         var bc = {
44939             handler: handler,
44940             scope: scope,
44941             minWidth: this.minButtonWidth,
44942             hideParent:true
44943         };
44944         if(typeof config == "string"){
44945             bc.text = config;
44946         }else{
44947             Roo.apply(bc, config);
44948         }
44949         var btn = new Roo.Button(null, bc);
44950         this.buttons.push(btn);
44951         return btn;
44952     },
44953
44954      /**
44955      * Adds a series of form elements (using the xtype property as the factory method.
44956      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44957      * @param {Object} config 
44958      */
44959     
44960     addxtype : function()
44961     {
44962         var ar = Array.prototype.slice.call(arguments, 0);
44963         var ret = false;
44964         for(var i = 0; i < ar.length; i++) {
44965             if (!ar[i]) {
44966                 continue; // skip -- if this happends something invalid got sent, we 
44967                 // should ignore it, as basically that interface element will not show up
44968                 // and that should be pretty obvious!!
44969             }
44970             
44971             if (Roo.form[ar[i].xtype]) {
44972                 ar[i].form = this;
44973                 var fe = Roo.factory(ar[i], Roo.form);
44974                 if (!ret) {
44975                     ret = fe;
44976                 }
44977                 fe.form = this;
44978                 if (fe.store) {
44979                     fe.store.form = this;
44980                 }
44981                 if (fe.isLayout) {  
44982                          
44983                     this.start(fe);
44984                     this.allItems.push(fe);
44985                     if (fe.items && fe.addxtype) {
44986                         fe.addxtype.apply(fe, fe.items);
44987                         delete fe.items;
44988                     }
44989                      this.end();
44990                     continue;
44991                 }
44992                 
44993                 
44994                  
44995                 this.add(fe);
44996               //  console.log('adding ' + ar[i].xtype);
44997             }
44998             if (ar[i].xtype == 'Button') {  
44999                 //console.log('adding button');
45000                 //console.log(ar[i]);
45001                 this.addButton(ar[i]);
45002                 this.allItems.push(fe);
45003                 continue;
45004             }
45005             
45006             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45007                 alert('end is not supported on xtype any more, use items');
45008             //    this.end();
45009             //    //console.log('adding end');
45010             }
45011             
45012         }
45013         return ret;
45014     },
45015     
45016     /**
45017      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45018      * option "monitorValid"
45019      */
45020     startMonitoring : function(){
45021         if(!this.bound){
45022             this.bound = true;
45023             Roo.TaskMgr.start({
45024                 run : this.bindHandler,
45025                 interval : this.monitorPoll || 200,
45026                 scope: this
45027             });
45028         }
45029     },
45030
45031     /**
45032      * Stops monitoring of the valid state of this form
45033      */
45034     stopMonitoring : function(){
45035         this.bound = false;
45036     },
45037
45038     // private
45039     bindHandler : function(){
45040         if(!this.bound){
45041             return false; // stops binding
45042         }
45043         var valid = true;
45044         this.items.each(function(f){
45045             if(!f.isValid(true)){
45046                 valid = false;
45047                 return false;
45048             }
45049         });
45050         for(var i = 0, len = this.buttons.length; i < len; i++){
45051             var btn = this.buttons[i];
45052             if(btn.formBind === true && btn.disabled === valid){
45053                 btn.setDisabled(!valid);
45054             }
45055         }
45056         this.fireEvent('clientvalidation', this, valid);
45057     }
45058     
45059     
45060     
45061     
45062     
45063     
45064     
45065     
45066 });
45067
45068
45069 // back compat
45070 Roo.Form = Roo.form.Form;
45071 /*
45072  * Based on:
45073  * Ext JS Library 1.1.1
45074  * Copyright(c) 2006-2007, Ext JS, LLC.
45075  *
45076  * Originally Released Under LGPL - original licence link has changed is not relivant.
45077  *
45078  * Fork - LGPL
45079  * <script type="text/javascript">
45080  */
45081
45082 // as we use this in bootstrap.
45083 Roo.namespace('Roo.form');
45084  /**
45085  * @class Roo.form.Action
45086  * Internal Class used to handle form actions
45087  * @constructor
45088  * @param {Roo.form.BasicForm} el The form element or its id
45089  * @param {Object} config Configuration options
45090  */
45091
45092  
45093  
45094 // define the action interface
45095 Roo.form.Action = function(form, options){
45096     this.form = form;
45097     this.options = options || {};
45098 };
45099 /**
45100  * Client Validation Failed
45101  * @const 
45102  */
45103 Roo.form.Action.CLIENT_INVALID = 'client';
45104 /**
45105  * Server Validation Failed
45106  * @const 
45107  */
45108 Roo.form.Action.SERVER_INVALID = 'server';
45109  /**
45110  * Connect to Server Failed
45111  * @const 
45112  */
45113 Roo.form.Action.CONNECT_FAILURE = 'connect';
45114 /**
45115  * Reading Data from Server Failed
45116  * @const 
45117  */
45118 Roo.form.Action.LOAD_FAILURE = 'load';
45119
45120 Roo.form.Action.prototype = {
45121     type : 'default',
45122     failureType : undefined,
45123     response : undefined,
45124     result : undefined,
45125
45126     // interface method
45127     run : function(options){
45128
45129     },
45130
45131     // interface method
45132     success : function(response){
45133
45134     },
45135
45136     // interface method
45137     handleResponse : function(response){
45138
45139     },
45140
45141     // default connection failure
45142     failure : function(response){
45143         
45144         this.response = response;
45145         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45146         this.form.afterAction(this, false);
45147     },
45148
45149     processResponse : function(response){
45150         this.response = response;
45151         if(!response.responseText){
45152             return true;
45153         }
45154         this.result = this.handleResponse(response);
45155         return this.result;
45156     },
45157
45158     // utility functions used internally
45159     getUrl : function(appendParams){
45160         var url = this.options.url || this.form.url || this.form.el.dom.action;
45161         if(appendParams){
45162             var p = this.getParams();
45163             if(p){
45164                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45165             }
45166         }
45167         return url;
45168     },
45169
45170     getMethod : function(){
45171         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45172     },
45173
45174     getParams : function(){
45175         var bp = this.form.baseParams;
45176         var p = this.options.params;
45177         if(p){
45178             if(typeof p == "object"){
45179                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45180             }else if(typeof p == 'string' && bp){
45181                 p += '&' + Roo.urlEncode(bp);
45182             }
45183         }else if(bp){
45184             p = Roo.urlEncode(bp);
45185         }
45186         return p;
45187     },
45188
45189     createCallback : function(){
45190         return {
45191             success: this.success,
45192             failure: this.failure,
45193             scope: this,
45194             timeout: (this.form.timeout*1000),
45195             upload: this.form.fileUpload ? this.success : undefined
45196         };
45197     }
45198 };
45199
45200 Roo.form.Action.Submit = function(form, options){
45201     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45202 };
45203
45204 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45205     type : 'submit',
45206
45207     haveProgress : false,
45208     uploadComplete : false,
45209     
45210     // uploadProgress indicator.
45211     uploadProgress : function()
45212     {
45213         if (!this.form.progressUrl) {
45214             return;
45215         }
45216         
45217         if (!this.haveProgress) {
45218             Roo.MessageBox.progress("Uploading", "Uploading");
45219         }
45220         if (this.uploadComplete) {
45221            Roo.MessageBox.hide();
45222            return;
45223         }
45224         
45225         this.haveProgress = true;
45226    
45227         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45228         
45229         var c = new Roo.data.Connection();
45230         c.request({
45231             url : this.form.progressUrl,
45232             params: {
45233                 id : uid
45234             },
45235             method: 'GET',
45236             success : function(req){
45237                //console.log(data);
45238                 var rdata = false;
45239                 var edata;
45240                 try  {
45241                    rdata = Roo.decode(req.responseText)
45242                 } catch (e) {
45243                     Roo.log("Invalid data from server..");
45244                     Roo.log(edata);
45245                     return;
45246                 }
45247                 if (!rdata || !rdata.success) {
45248                     Roo.log(rdata);
45249                     Roo.MessageBox.alert(Roo.encode(rdata));
45250                     return;
45251                 }
45252                 var data = rdata.data;
45253                 
45254                 if (this.uploadComplete) {
45255                    Roo.MessageBox.hide();
45256                    return;
45257                 }
45258                    
45259                 if (data){
45260                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45261                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45262                     );
45263                 }
45264                 this.uploadProgress.defer(2000,this);
45265             },
45266        
45267             failure: function(data) {
45268                 Roo.log('progress url failed ');
45269                 Roo.log(data);
45270             },
45271             scope : this
45272         });
45273            
45274     },
45275     
45276     
45277     run : function()
45278     {
45279         // run get Values on the form, so it syncs any secondary forms.
45280         this.form.getValues();
45281         
45282         var o = this.options;
45283         var method = this.getMethod();
45284         var isPost = method == 'POST';
45285         if(o.clientValidation === false || this.form.isValid()){
45286             
45287             if (this.form.progressUrl) {
45288                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45289                     (new Date() * 1) + '' + Math.random());
45290                     
45291             } 
45292             
45293             
45294             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45295                 form:this.form.el.dom,
45296                 url:this.getUrl(!isPost),
45297                 method: method,
45298                 params:isPost ? this.getParams() : null,
45299                 isUpload: this.form.fileUpload
45300             }));
45301             
45302             this.uploadProgress();
45303
45304         }else if (o.clientValidation !== false){ // client validation failed
45305             this.failureType = Roo.form.Action.CLIENT_INVALID;
45306             this.form.afterAction(this, false);
45307         }
45308     },
45309
45310     success : function(response)
45311     {
45312         this.uploadComplete= true;
45313         if (this.haveProgress) {
45314             Roo.MessageBox.hide();
45315         }
45316         
45317         
45318         var result = this.processResponse(response);
45319         if(result === true || result.success){
45320             this.form.afterAction(this, true);
45321             return;
45322         }
45323         if(result.errors){
45324             this.form.markInvalid(result.errors);
45325             this.failureType = Roo.form.Action.SERVER_INVALID;
45326         }
45327         this.form.afterAction(this, false);
45328     },
45329     failure : function(response)
45330     {
45331         this.uploadComplete= true;
45332         if (this.haveProgress) {
45333             Roo.MessageBox.hide();
45334         }
45335         
45336         this.response = response;
45337         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45338         this.form.afterAction(this, false);
45339     },
45340     
45341     handleResponse : function(response){
45342         if(this.form.errorReader){
45343             var rs = this.form.errorReader.read(response);
45344             var errors = [];
45345             if(rs.records){
45346                 for(var i = 0, len = rs.records.length; i < len; i++) {
45347                     var r = rs.records[i];
45348                     errors[i] = r.data;
45349                 }
45350             }
45351             if(errors.length < 1){
45352                 errors = null;
45353             }
45354             return {
45355                 success : rs.success,
45356                 errors : errors
45357             };
45358         }
45359         var ret = false;
45360         try {
45361             ret = Roo.decode(response.responseText);
45362         } catch (e) {
45363             ret = {
45364                 success: false,
45365                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45366                 errors : []
45367             };
45368         }
45369         return ret;
45370         
45371     }
45372 });
45373
45374
45375 Roo.form.Action.Load = function(form, options){
45376     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45377     this.reader = this.form.reader;
45378 };
45379
45380 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45381     type : 'load',
45382
45383     run : function(){
45384         
45385         Roo.Ajax.request(Roo.apply(
45386                 this.createCallback(), {
45387                     method:this.getMethod(),
45388                     url:this.getUrl(false),
45389                     params:this.getParams()
45390         }));
45391     },
45392
45393     success : function(response){
45394         
45395         var result = this.processResponse(response);
45396         if(result === true || !result.success || !result.data){
45397             this.failureType = Roo.form.Action.LOAD_FAILURE;
45398             this.form.afterAction(this, false);
45399             return;
45400         }
45401         this.form.clearInvalid();
45402         this.form.setValues(result.data);
45403         this.form.afterAction(this, true);
45404     },
45405
45406     handleResponse : function(response){
45407         if(this.form.reader){
45408             var rs = this.form.reader.read(response);
45409             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45410             return {
45411                 success : rs.success,
45412                 data : data
45413             };
45414         }
45415         return Roo.decode(response.responseText);
45416     }
45417 });
45418
45419 Roo.form.Action.ACTION_TYPES = {
45420     'load' : Roo.form.Action.Load,
45421     'submit' : Roo.form.Action.Submit
45422 };/*
45423  * Based on:
45424  * Ext JS Library 1.1.1
45425  * Copyright(c) 2006-2007, Ext JS, LLC.
45426  *
45427  * Originally Released Under LGPL - original licence link has changed is not relivant.
45428  *
45429  * Fork - LGPL
45430  * <script type="text/javascript">
45431  */
45432  
45433 /**
45434  * @class Roo.form.Layout
45435  * @extends Roo.Component
45436  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45437  * @constructor
45438  * @param {Object} config Configuration options
45439  */
45440 Roo.form.Layout = function(config){
45441     var xitems = [];
45442     if (config.items) {
45443         xitems = config.items;
45444         delete config.items;
45445     }
45446     Roo.form.Layout.superclass.constructor.call(this, config);
45447     this.stack = [];
45448     Roo.each(xitems, this.addxtype, this);
45449      
45450 };
45451
45452 Roo.extend(Roo.form.Layout, Roo.Component, {
45453     /**
45454      * @cfg {String/Object} autoCreate
45455      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45456      */
45457     /**
45458      * @cfg {String/Object/Function} style
45459      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45460      * a function which returns such a specification.
45461      */
45462     /**
45463      * @cfg {String} labelAlign
45464      * Valid values are "left," "top" and "right" (defaults to "left")
45465      */
45466     /**
45467      * @cfg {Number} labelWidth
45468      * Fixed width in pixels of all field labels (defaults to undefined)
45469      */
45470     /**
45471      * @cfg {Boolean} clear
45472      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45473      */
45474     clear : true,
45475     /**
45476      * @cfg {String} labelSeparator
45477      * The separator to use after field labels (defaults to ':')
45478      */
45479     labelSeparator : ':',
45480     /**
45481      * @cfg {Boolean} hideLabels
45482      * True to suppress the display of field labels in this layout (defaults to false)
45483      */
45484     hideLabels : false,
45485
45486     // private
45487     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45488     
45489     isLayout : true,
45490     
45491     // private
45492     onRender : function(ct, position){
45493         if(this.el){ // from markup
45494             this.el = Roo.get(this.el);
45495         }else {  // generate
45496             var cfg = this.getAutoCreate();
45497             this.el = ct.createChild(cfg, position);
45498         }
45499         if(this.style){
45500             this.el.applyStyles(this.style);
45501         }
45502         if(this.labelAlign){
45503             this.el.addClass('x-form-label-'+this.labelAlign);
45504         }
45505         if(this.hideLabels){
45506             this.labelStyle = "display:none";
45507             this.elementStyle = "padding-left:0;";
45508         }else{
45509             if(typeof this.labelWidth == 'number'){
45510                 this.labelStyle = "width:"+this.labelWidth+"px;";
45511                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45512             }
45513             if(this.labelAlign == 'top'){
45514                 this.labelStyle = "width:auto;";
45515                 this.elementStyle = "padding-left:0;";
45516             }
45517         }
45518         var stack = this.stack;
45519         var slen = stack.length;
45520         if(slen > 0){
45521             if(!this.fieldTpl){
45522                 var t = new Roo.Template(
45523                     '<div class="x-form-item {5}">',
45524                         '<label for="{0}" style="{2}">{1}{4}</label>',
45525                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45526                         '</div>',
45527                     '</div><div class="x-form-clear-left"></div>'
45528                 );
45529                 t.disableFormats = true;
45530                 t.compile();
45531                 Roo.form.Layout.prototype.fieldTpl = t;
45532             }
45533             for(var i = 0; i < slen; i++) {
45534                 if(stack[i].isFormField){
45535                     this.renderField(stack[i]);
45536                 }else{
45537                     this.renderComponent(stack[i]);
45538                 }
45539             }
45540         }
45541         if(this.clear){
45542             this.el.createChild({cls:'x-form-clear'});
45543         }
45544     },
45545
45546     // private
45547     renderField : function(f){
45548         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45549                f.id, //0
45550                f.fieldLabel, //1
45551                f.labelStyle||this.labelStyle||'', //2
45552                this.elementStyle||'', //3
45553                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45554                f.itemCls||this.itemCls||''  //5
45555        ], true).getPrevSibling());
45556     },
45557
45558     // private
45559     renderComponent : function(c){
45560         c.render(c.isLayout ? this.el : this.el.createChild());    
45561     },
45562     /**
45563      * Adds a object form elements (using the xtype property as the factory method.)
45564      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45565      * @param {Object} config 
45566      */
45567     addxtype : function(o)
45568     {
45569         // create the lement.
45570         o.form = this.form;
45571         var fe = Roo.factory(o, Roo.form);
45572         this.form.allItems.push(fe);
45573         this.stack.push(fe);
45574         
45575         if (fe.isFormField) {
45576             this.form.items.add(fe);
45577         }
45578          
45579         return fe;
45580     }
45581 });
45582
45583 /**
45584  * @class Roo.form.Column
45585  * @extends Roo.form.Layout
45586  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45587  * @constructor
45588  * @param {Object} config Configuration options
45589  */
45590 Roo.form.Column = function(config){
45591     Roo.form.Column.superclass.constructor.call(this, config);
45592 };
45593
45594 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45595     /**
45596      * @cfg {Number/String} width
45597      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45598      */
45599     /**
45600      * @cfg {String/Object} autoCreate
45601      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45602      */
45603
45604     // private
45605     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45606
45607     // private
45608     onRender : function(ct, position){
45609         Roo.form.Column.superclass.onRender.call(this, ct, position);
45610         if(this.width){
45611             this.el.setWidth(this.width);
45612         }
45613     }
45614 });
45615
45616
45617 /**
45618  * @class Roo.form.Row
45619  * @extends Roo.form.Layout
45620  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45621  * @constructor
45622  * @param {Object} config Configuration options
45623  */
45624
45625  
45626 Roo.form.Row = function(config){
45627     Roo.form.Row.superclass.constructor.call(this, config);
45628 };
45629  
45630 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45631       /**
45632      * @cfg {Number/String} width
45633      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45634      */
45635     /**
45636      * @cfg {Number/String} height
45637      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45638      */
45639     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45640     
45641     padWidth : 20,
45642     // private
45643     onRender : function(ct, position){
45644         //console.log('row render');
45645         if(!this.rowTpl){
45646             var t = new Roo.Template(
45647                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45648                     '<label for="{0}" style="{2}">{1}{4}</label>',
45649                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45650                     '</div>',
45651                 '</div>'
45652             );
45653             t.disableFormats = true;
45654             t.compile();
45655             Roo.form.Layout.prototype.rowTpl = t;
45656         }
45657         this.fieldTpl = this.rowTpl;
45658         
45659         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45660         var labelWidth = 100;
45661         
45662         if ((this.labelAlign != 'top')) {
45663             if (typeof this.labelWidth == 'number') {
45664                 labelWidth = this.labelWidth
45665             }
45666             this.padWidth =  20 + labelWidth;
45667             
45668         }
45669         
45670         Roo.form.Column.superclass.onRender.call(this, ct, position);
45671         if(this.width){
45672             this.el.setWidth(this.width);
45673         }
45674         if(this.height){
45675             this.el.setHeight(this.height);
45676         }
45677     },
45678     
45679     // private
45680     renderField : function(f){
45681         f.fieldEl = this.fieldTpl.append(this.el, [
45682                f.id, f.fieldLabel,
45683                f.labelStyle||this.labelStyle||'',
45684                this.elementStyle||'',
45685                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45686                f.itemCls||this.itemCls||'',
45687                f.width ? f.width + this.padWidth : 160 + this.padWidth
45688        ],true);
45689     }
45690 });
45691  
45692
45693 /**
45694  * @class Roo.form.FieldSet
45695  * @extends Roo.form.Layout
45696  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45697  * @constructor
45698  * @param {Object} config Configuration options
45699  */
45700 Roo.form.FieldSet = function(config){
45701     Roo.form.FieldSet.superclass.constructor.call(this, config);
45702 };
45703
45704 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45705     /**
45706      * @cfg {String} legend
45707      * The text to display as the legend for the FieldSet (defaults to '')
45708      */
45709     /**
45710      * @cfg {String/Object} autoCreate
45711      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45712      */
45713
45714     // private
45715     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45716
45717     // private
45718     onRender : function(ct, position){
45719         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45720         if(this.legend){
45721             this.setLegend(this.legend);
45722         }
45723     },
45724
45725     // private
45726     setLegend : function(text){
45727         if(this.rendered){
45728             this.el.child('legend').update(text);
45729         }
45730     }
45731 });/*
45732  * Based on:
45733  * Ext JS Library 1.1.1
45734  * Copyright(c) 2006-2007, Ext JS, LLC.
45735  *
45736  * Originally Released Under LGPL - original licence link has changed is not relivant.
45737  *
45738  * Fork - LGPL
45739  * <script type="text/javascript">
45740  */
45741 /**
45742  * @class Roo.form.VTypes
45743  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45744  * @singleton
45745  */
45746 Roo.form.VTypes = function(){
45747     // closure these in so they are only created once.
45748     var alpha = /^[a-zA-Z_]+$/;
45749     var alphanum = /^[a-zA-Z0-9_]+$/;
45750     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45751     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45752
45753     // All these messages and functions are configurable
45754     return {
45755         /**
45756          * The function used to validate email addresses
45757          * @param {String} value The email address
45758          */
45759         'email' : function(v){
45760             return email.test(v);
45761         },
45762         /**
45763          * The error text to display when the email validation function returns false
45764          * @type String
45765          */
45766         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45767         /**
45768          * The keystroke filter mask to be applied on email input
45769          * @type RegExp
45770          */
45771         'emailMask' : /[a-z0-9_\.\-@]/i,
45772
45773         /**
45774          * The function used to validate URLs
45775          * @param {String} value The URL
45776          */
45777         'url' : function(v){
45778             return url.test(v);
45779         },
45780         /**
45781          * The error text to display when the url validation function returns false
45782          * @type String
45783          */
45784         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45785         
45786         /**
45787          * The function used to validate alpha values
45788          * @param {String} value The value
45789          */
45790         'alpha' : function(v){
45791             return alpha.test(v);
45792         },
45793         /**
45794          * The error text to display when the alpha validation function returns false
45795          * @type String
45796          */
45797         'alphaText' : 'This field should only contain letters and _',
45798         /**
45799          * The keystroke filter mask to be applied on alpha input
45800          * @type RegExp
45801          */
45802         'alphaMask' : /[a-z_]/i,
45803
45804         /**
45805          * The function used to validate alphanumeric values
45806          * @param {String} value The value
45807          */
45808         'alphanum' : function(v){
45809             return alphanum.test(v);
45810         },
45811         /**
45812          * The error text to display when the alphanumeric validation function returns false
45813          * @type String
45814          */
45815         'alphanumText' : 'This field should only contain letters, numbers and _',
45816         /**
45817          * The keystroke filter mask to be applied on alphanumeric input
45818          * @type RegExp
45819          */
45820         'alphanumMask' : /[a-z0-9_]/i
45821     };
45822 }();//<script type="text/javascript">
45823
45824 /**
45825  * @class Roo.form.FCKeditor
45826  * @extends Roo.form.TextArea
45827  * Wrapper around the FCKEditor http://www.fckeditor.net
45828  * @constructor
45829  * Creates a new FCKeditor
45830  * @param {Object} config Configuration options
45831  */
45832 Roo.form.FCKeditor = function(config){
45833     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45834     this.addEvents({
45835          /**
45836          * @event editorinit
45837          * Fired when the editor is initialized - you can add extra handlers here..
45838          * @param {FCKeditor} this
45839          * @param {Object} the FCK object.
45840          */
45841         editorinit : true
45842     });
45843     
45844     
45845 };
45846 Roo.form.FCKeditor.editors = { };
45847 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45848 {
45849     //defaultAutoCreate : {
45850     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45851     //},
45852     // private
45853     /**
45854      * @cfg {Object} fck options - see fck manual for details.
45855      */
45856     fckconfig : false,
45857     
45858     /**
45859      * @cfg {Object} fck toolbar set (Basic or Default)
45860      */
45861     toolbarSet : 'Basic',
45862     /**
45863      * @cfg {Object} fck BasePath
45864      */ 
45865     basePath : '/fckeditor/',
45866     
45867     
45868     frame : false,
45869     
45870     value : '',
45871     
45872    
45873     onRender : function(ct, position)
45874     {
45875         if(!this.el){
45876             this.defaultAutoCreate = {
45877                 tag: "textarea",
45878                 style:"width:300px;height:60px;",
45879                 autocomplete: "off"
45880             };
45881         }
45882         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45883         /*
45884         if(this.grow){
45885             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45886             if(this.preventScrollbars){
45887                 this.el.setStyle("overflow", "hidden");
45888             }
45889             this.el.setHeight(this.growMin);
45890         }
45891         */
45892         //console.log('onrender' + this.getId() );
45893         Roo.form.FCKeditor.editors[this.getId()] = this;
45894          
45895
45896         this.replaceTextarea() ;
45897         
45898     },
45899     
45900     getEditor : function() {
45901         return this.fckEditor;
45902     },
45903     /**
45904      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45905      * @param {Mixed} value The value to set
45906      */
45907     
45908     
45909     setValue : function(value)
45910     {
45911         //console.log('setValue: ' + value);
45912         
45913         if(typeof(value) == 'undefined') { // not sure why this is happending...
45914             return;
45915         }
45916         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45917         
45918         //if(!this.el || !this.getEditor()) {
45919         //    this.value = value;
45920             //this.setValue.defer(100,this,[value]);    
45921         //    return;
45922         //} 
45923         
45924         if(!this.getEditor()) {
45925             return;
45926         }
45927         
45928         this.getEditor().SetData(value);
45929         
45930         //
45931
45932     },
45933
45934     /**
45935      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45936      * @return {Mixed} value The field value
45937      */
45938     getValue : function()
45939     {
45940         
45941         if (this.frame && this.frame.dom.style.display == 'none') {
45942             return Roo.form.FCKeditor.superclass.getValue.call(this);
45943         }
45944         
45945         if(!this.el || !this.getEditor()) {
45946            
45947            // this.getValue.defer(100,this); 
45948             return this.value;
45949         }
45950        
45951         
45952         var value=this.getEditor().GetData();
45953         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45954         return Roo.form.FCKeditor.superclass.getValue.call(this);
45955         
45956
45957     },
45958
45959     /**
45960      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45961      * @return {Mixed} value The field value
45962      */
45963     getRawValue : function()
45964     {
45965         if (this.frame && this.frame.dom.style.display == 'none') {
45966             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45967         }
45968         
45969         if(!this.el || !this.getEditor()) {
45970             //this.getRawValue.defer(100,this); 
45971             return this.value;
45972             return;
45973         }
45974         
45975         
45976         
45977         var value=this.getEditor().GetData();
45978         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45979         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45980          
45981     },
45982     
45983     setSize : function(w,h) {
45984         
45985         
45986         
45987         //if (this.frame && this.frame.dom.style.display == 'none') {
45988         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45989         //    return;
45990         //}
45991         //if(!this.el || !this.getEditor()) {
45992         //    this.setSize.defer(100,this, [w,h]); 
45993         //    return;
45994         //}
45995         
45996         
45997         
45998         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45999         
46000         this.frame.dom.setAttribute('width', w);
46001         this.frame.dom.setAttribute('height', h);
46002         this.frame.setSize(w,h);
46003         
46004     },
46005     
46006     toggleSourceEdit : function(value) {
46007         
46008       
46009          
46010         this.el.dom.style.display = value ? '' : 'none';
46011         this.frame.dom.style.display = value ?  'none' : '';
46012         
46013     },
46014     
46015     
46016     focus: function(tag)
46017     {
46018         if (this.frame.dom.style.display == 'none') {
46019             return Roo.form.FCKeditor.superclass.focus.call(this);
46020         }
46021         if(!this.el || !this.getEditor()) {
46022             this.focus.defer(100,this, [tag]); 
46023             return;
46024         }
46025         
46026         
46027         
46028         
46029         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46030         this.getEditor().Focus();
46031         if (tgs.length) {
46032             if (!this.getEditor().Selection.GetSelection()) {
46033                 this.focus.defer(100,this, [tag]); 
46034                 return;
46035             }
46036             
46037             
46038             var r = this.getEditor().EditorDocument.createRange();
46039             r.setStart(tgs[0],0);
46040             r.setEnd(tgs[0],0);
46041             this.getEditor().Selection.GetSelection().removeAllRanges();
46042             this.getEditor().Selection.GetSelection().addRange(r);
46043             this.getEditor().Focus();
46044         }
46045         
46046     },
46047     
46048     
46049     
46050     replaceTextarea : function()
46051     {
46052         if ( document.getElementById( this.getId() + '___Frame' ) )
46053             return ;
46054         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46055         //{
46056             // We must check the elements firstly using the Id and then the name.
46057         var oTextarea = document.getElementById( this.getId() );
46058         
46059         var colElementsByName = document.getElementsByName( this.getId() ) ;
46060          
46061         oTextarea.style.display = 'none' ;
46062
46063         if ( oTextarea.tabIndex ) {            
46064             this.TabIndex = oTextarea.tabIndex ;
46065         }
46066         
46067         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46068         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46069         this.frame = Roo.get(this.getId() + '___Frame')
46070     },
46071     
46072     _getConfigHtml : function()
46073     {
46074         var sConfig = '' ;
46075
46076         for ( var o in this.fckconfig ) {
46077             sConfig += sConfig.length > 0  ? '&amp;' : '';
46078             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46079         }
46080
46081         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46082     },
46083     
46084     
46085     _getIFrameHtml : function()
46086     {
46087         var sFile = 'fckeditor.html' ;
46088         /* no idea what this is about..
46089         try
46090         {
46091             if ( (/fcksource=true/i).test( window.top.location.search ) )
46092                 sFile = 'fckeditor.original.html' ;
46093         }
46094         catch (e) { 
46095         */
46096
46097         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46098         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46099         
46100         
46101         var html = '<iframe id="' + this.getId() +
46102             '___Frame" src="' + sLink +
46103             '" width="' + this.width +
46104             '" height="' + this.height + '"' +
46105             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46106             ' frameborder="0" scrolling="no"></iframe>' ;
46107
46108         return html ;
46109     },
46110     
46111     _insertHtmlBefore : function( html, element )
46112     {
46113         if ( element.insertAdjacentHTML )       {
46114             // IE
46115             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46116         } else { // Gecko
46117             var oRange = document.createRange() ;
46118             oRange.setStartBefore( element ) ;
46119             var oFragment = oRange.createContextualFragment( html );
46120             element.parentNode.insertBefore( oFragment, element ) ;
46121         }
46122     }
46123     
46124     
46125   
46126     
46127     
46128     
46129     
46130
46131 });
46132
46133 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46134
46135 function FCKeditor_OnComplete(editorInstance){
46136     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46137     f.fckEditor = editorInstance;
46138     //console.log("loaded");
46139     f.fireEvent('editorinit', f, editorInstance);
46140
46141   
46142
46143  
46144
46145
46146
46147
46148
46149
46150
46151
46152
46153
46154
46155
46156
46157
46158
46159 //<script type="text/javascript">
46160 /**
46161  * @class Roo.form.GridField
46162  * @extends Roo.form.Field
46163  * Embed a grid (or editable grid into a form)
46164  * STATUS ALPHA
46165  * 
46166  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46167  * it needs 
46168  * xgrid.store = Roo.data.Store
46169  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46170  * xgrid.store.reader = Roo.data.JsonReader 
46171  * 
46172  * 
46173  * @constructor
46174  * Creates a new GridField
46175  * @param {Object} config Configuration options
46176  */
46177 Roo.form.GridField = function(config){
46178     Roo.form.GridField.superclass.constructor.call(this, config);
46179      
46180 };
46181
46182 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46183     /**
46184      * @cfg {Number} width  - used to restrict width of grid..
46185      */
46186     width : 100,
46187     /**
46188      * @cfg {Number} height - used to restrict height of grid..
46189      */
46190     height : 50,
46191      /**
46192      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46193          * 
46194          *}
46195      */
46196     xgrid : false, 
46197     /**
46198      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46199      * {tag: "input", type: "checkbox", autocomplete: "off"})
46200      */
46201    // defaultAutoCreate : { tag: 'div' },
46202     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46203     /**
46204      * @cfg {String} addTitle Text to include for adding a title.
46205      */
46206     addTitle : false,
46207     //
46208     onResize : function(){
46209         Roo.form.Field.superclass.onResize.apply(this, arguments);
46210     },
46211
46212     initEvents : function(){
46213         // Roo.form.Checkbox.superclass.initEvents.call(this);
46214         // has no events...
46215        
46216     },
46217
46218
46219     getResizeEl : function(){
46220         return this.wrap;
46221     },
46222
46223     getPositionEl : function(){
46224         return this.wrap;
46225     },
46226
46227     // private
46228     onRender : function(ct, position){
46229         
46230         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46231         var style = this.style;
46232         delete this.style;
46233         
46234         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46235         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46236         this.viewEl = this.wrap.createChild({ tag: 'div' });
46237         if (style) {
46238             this.viewEl.applyStyles(style);
46239         }
46240         if (this.width) {
46241             this.viewEl.setWidth(this.width);
46242         }
46243         if (this.height) {
46244             this.viewEl.setHeight(this.height);
46245         }
46246         //if(this.inputValue !== undefined){
46247         //this.setValue(this.value);
46248         
46249         
46250         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46251         
46252         
46253         this.grid.render();
46254         this.grid.getDataSource().on('remove', this.refreshValue, this);
46255         this.grid.getDataSource().on('update', this.refreshValue, this);
46256         this.grid.on('afteredit', this.refreshValue, this);
46257  
46258     },
46259      
46260     
46261     /**
46262      * Sets the value of the item. 
46263      * @param {String} either an object  or a string..
46264      */
46265     setValue : function(v){
46266         //this.value = v;
46267         v = v || []; // empty set..
46268         // this does not seem smart - it really only affects memoryproxy grids..
46269         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46270             var ds = this.grid.getDataSource();
46271             // assumes a json reader..
46272             var data = {}
46273             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46274             ds.loadData( data);
46275         }
46276         // clear selection so it does not get stale.
46277         if (this.grid.sm) { 
46278             this.grid.sm.clearSelections();
46279         }
46280         
46281         Roo.form.GridField.superclass.setValue.call(this, v);
46282         this.refreshValue();
46283         // should load data in the grid really....
46284     },
46285     
46286     // private
46287     refreshValue: function() {
46288          var val = [];
46289         this.grid.getDataSource().each(function(r) {
46290             val.push(r.data);
46291         });
46292         this.el.dom.value = Roo.encode(val);
46293     }
46294     
46295      
46296     
46297     
46298 });/*
46299  * Based on:
46300  * Ext JS Library 1.1.1
46301  * Copyright(c) 2006-2007, Ext JS, LLC.
46302  *
46303  * Originally Released Under LGPL - original licence link has changed is not relivant.
46304  *
46305  * Fork - LGPL
46306  * <script type="text/javascript">
46307  */
46308 /**
46309  * @class Roo.form.DisplayField
46310  * @extends Roo.form.Field
46311  * A generic Field to display non-editable data.
46312  * @constructor
46313  * Creates a new Display Field item.
46314  * @param {Object} config Configuration options
46315  */
46316 Roo.form.DisplayField = function(config){
46317     Roo.form.DisplayField.superclass.constructor.call(this, config);
46318     
46319 };
46320
46321 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46322     inputType:      'hidden',
46323     allowBlank:     true,
46324     readOnly:         true,
46325     
46326  
46327     /**
46328      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46329      */
46330     focusClass : undefined,
46331     /**
46332      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46333      */
46334     fieldClass: 'x-form-field',
46335     
46336      /**
46337      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46338      */
46339     valueRenderer: undefined,
46340     
46341     width: 100,
46342     /**
46343      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46344      * {tag: "input", type: "checkbox", autocomplete: "off"})
46345      */
46346      
46347  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46348
46349     onResize : function(){
46350         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46351         
46352     },
46353
46354     initEvents : function(){
46355         // Roo.form.Checkbox.superclass.initEvents.call(this);
46356         // has no events...
46357        
46358     },
46359
46360
46361     getResizeEl : function(){
46362         return this.wrap;
46363     },
46364
46365     getPositionEl : function(){
46366         return this.wrap;
46367     },
46368
46369     // private
46370     onRender : function(ct, position){
46371         
46372         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46373         //if(this.inputValue !== undefined){
46374         this.wrap = this.el.wrap();
46375         
46376         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46377         
46378         if (this.bodyStyle) {
46379             this.viewEl.applyStyles(this.bodyStyle);
46380         }
46381         //this.viewEl.setStyle('padding', '2px');
46382         
46383         this.setValue(this.value);
46384         
46385     },
46386 /*
46387     // private
46388     initValue : Roo.emptyFn,
46389
46390   */
46391
46392         // private
46393     onClick : function(){
46394         
46395     },
46396
46397     /**
46398      * Sets the checked state of the checkbox.
46399      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46400      */
46401     setValue : function(v){
46402         this.value = v;
46403         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46404         // this might be called before we have a dom element..
46405         if (!this.viewEl) {
46406             return;
46407         }
46408         this.viewEl.dom.innerHTML = html;
46409         Roo.form.DisplayField.superclass.setValue.call(this, v);
46410
46411     }
46412 });/*
46413  * 
46414  * Licence- LGPL
46415  * 
46416  */
46417
46418 /**
46419  * @class Roo.form.DayPicker
46420  * @extends Roo.form.Field
46421  * A Day picker show [M] [T] [W] ....
46422  * @constructor
46423  * Creates a new Day Picker
46424  * @param {Object} config Configuration options
46425  */
46426 Roo.form.DayPicker= function(config){
46427     Roo.form.DayPicker.superclass.constructor.call(this, config);
46428      
46429 };
46430
46431 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46432     /**
46433      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46434      */
46435     focusClass : undefined,
46436     /**
46437      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46438      */
46439     fieldClass: "x-form-field",
46440    
46441     /**
46442      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46443      * {tag: "input", type: "checkbox", autocomplete: "off"})
46444      */
46445     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46446     
46447    
46448     actionMode : 'viewEl', 
46449     //
46450     // private
46451  
46452     inputType : 'hidden',
46453     
46454      
46455     inputElement: false, // real input element?
46456     basedOn: false, // ????
46457     
46458     isFormField: true, // not sure where this is needed!!!!
46459
46460     onResize : function(){
46461         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46462         if(!this.boxLabel){
46463             this.el.alignTo(this.wrap, 'c-c');
46464         }
46465     },
46466
46467     initEvents : function(){
46468         Roo.form.Checkbox.superclass.initEvents.call(this);
46469         this.el.on("click", this.onClick,  this);
46470         this.el.on("change", this.onClick,  this);
46471     },
46472
46473
46474     getResizeEl : function(){
46475         return this.wrap;
46476     },
46477
46478     getPositionEl : function(){
46479         return this.wrap;
46480     },
46481
46482     
46483     // private
46484     onRender : function(ct, position){
46485         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46486        
46487         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46488         
46489         var r1 = '<table><tr>';
46490         var r2 = '<tr class="x-form-daypick-icons">';
46491         for (var i=0; i < 7; i++) {
46492             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46493             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46494         }
46495         
46496         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46497         viewEl.select('img').on('click', this.onClick, this);
46498         this.viewEl = viewEl;   
46499         
46500         
46501         // this will not work on Chrome!!!
46502         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46503         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46504         
46505         
46506           
46507
46508     },
46509
46510     // private
46511     initValue : Roo.emptyFn,
46512
46513     /**
46514      * Returns the checked state of the checkbox.
46515      * @return {Boolean} True if checked, else false
46516      */
46517     getValue : function(){
46518         return this.el.dom.value;
46519         
46520     },
46521
46522         // private
46523     onClick : function(e){ 
46524         //this.setChecked(!this.checked);
46525         Roo.get(e.target).toggleClass('x-menu-item-checked');
46526         this.refreshValue();
46527         //if(this.el.dom.checked != this.checked){
46528         //    this.setValue(this.el.dom.checked);
46529        // }
46530     },
46531     
46532     // private
46533     refreshValue : function()
46534     {
46535         var val = '';
46536         this.viewEl.select('img',true).each(function(e,i,n)  {
46537             val += e.is(".x-menu-item-checked") ? String(n) : '';
46538         });
46539         this.setValue(val, true);
46540     },
46541
46542     /**
46543      * Sets the checked state of the checkbox.
46544      * On is always based on a string comparison between inputValue and the param.
46545      * @param {Boolean/String} value - the value to set 
46546      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46547      */
46548     setValue : function(v,suppressEvent){
46549         if (!this.el.dom) {
46550             return;
46551         }
46552         var old = this.el.dom.value ;
46553         this.el.dom.value = v;
46554         if (suppressEvent) {
46555             return ;
46556         }
46557          
46558         // update display..
46559         this.viewEl.select('img',true).each(function(e,i,n)  {
46560             
46561             var on = e.is(".x-menu-item-checked");
46562             var newv = v.indexOf(String(n)) > -1;
46563             if (on != newv) {
46564                 e.toggleClass('x-menu-item-checked');
46565             }
46566             
46567         });
46568         
46569         
46570         this.fireEvent('change', this, v, old);
46571         
46572         
46573     },
46574    
46575     // handle setting of hidden value by some other method!!?!?
46576     setFromHidden: function()
46577     {
46578         if(!this.el){
46579             return;
46580         }
46581         //console.log("SET FROM HIDDEN");
46582         //alert('setFrom hidden');
46583         this.setValue(this.el.dom.value);
46584     },
46585     
46586     onDestroy : function()
46587     {
46588         if(this.viewEl){
46589             Roo.get(this.viewEl).remove();
46590         }
46591          
46592         Roo.form.DayPicker.superclass.onDestroy.call(this);
46593     }
46594
46595 });/*
46596  * RooJS Library 1.1.1
46597  * Copyright(c) 2008-2011  Alan Knowles
46598  *
46599  * License - LGPL
46600  */
46601  
46602
46603 /**
46604  * @class Roo.form.ComboCheck
46605  * @extends Roo.form.ComboBox
46606  * A combobox for multiple select items.
46607  *
46608  * FIXME - could do with a reset button..
46609  * 
46610  * @constructor
46611  * Create a new ComboCheck
46612  * @param {Object} config Configuration options
46613  */
46614 Roo.form.ComboCheck = function(config){
46615     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46616     // should verify some data...
46617     // like
46618     // hiddenName = required..
46619     // displayField = required
46620     // valudField == required
46621     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46622     var _t = this;
46623     Roo.each(req, function(e) {
46624         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46625             throw "Roo.form.ComboCheck : missing value for: " + e;
46626         }
46627     });
46628     
46629     
46630 };
46631
46632 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46633      
46634      
46635     editable : false,
46636      
46637     selectedClass: 'x-menu-item-checked', 
46638     
46639     // private
46640     onRender : function(ct, position){
46641         var _t = this;
46642         
46643         
46644         
46645         if(!this.tpl){
46646             var cls = 'x-combo-list';
46647
46648             
46649             this.tpl =  new Roo.Template({
46650                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46651                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46652                    '<span>{' + this.displayField + '}</span>' +
46653                     '</div>' 
46654                 
46655             });
46656         }
46657  
46658         
46659         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46660         this.view.singleSelect = false;
46661         this.view.multiSelect = true;
46662         this.view.toggleSelect = true;
46663         this.pageTb.add(new Roo.Toolbar.Fill(), {
46664             
46665             text: 'Done',
46666             handler: function()
46667             {
46668                 _t.collapse();
46669             }
46670         });
46671     },
46672     
46673     onViewOver : function(e, t){
46674         // do nothing...
46675         return;
46676         
46677     },
46678     
46679     onViewClick : function(doFocus,index){
46680         return;
46681         
46682     },
46683     select: function () {
46684         //Roo.log("SELECT CALLED");
46685     },
46686      
46687     selectByValue : function(xv, scrollIntoView){
46688         var ar = this.getValueArray();
46689         var sels = [];
46690         
46691         Roo.each(ar, function(v) {
46692             if(v === undefined || v === null){
46693                 return;
46694             }
46695             var r = this.findRecord(this.valueField, v);
46696             if(r){
46697                 sels.push(this.store.indexOf(r))
46698                 
46699             }
46700         },this);
46701         this.view.select(sels);
46702         return false;
46703     },
46704     
46705     
46706     
46707     onSelect : function(record, index){
46708        // Roo.log("onselect Called");
46709        // this is only called by the clear button now..
46710         this.view.clearSelections();
46711         this.setValue('[]');
46712         if (this.value != this.valueBefore) {
46713             this.fireEvent('change', this, this.value, this.valueBefore);
46714             this.valueBefore = this.value;
46715         }
46716     },
46717     getValueArray : function()
46718     {
46719         var ar = [] ;
46720         
46721         try {
46722             //Roo.log(this.value);
46723             if (typeof(this.value) == 'undefined') {
46724                 return [];
46725             }
46726             var ar = Roo.decode(this.value);
46727             return  ar instanceof Array ? ar : []; //?? valid?
46728             
46729         } catch(e) {
46730             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46731             return [];
46732         }
46733          
46734     },
46735     expand : function ()
46736     {
46737         
46738         Roo.form.ComboCheck.superclass.expand.call(this);
46739         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46740         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46741         
46742
46743     },
46744     
46745     collapse : function(){
46746         Roo.form.ComboCheck.superclass.collapse.call(this);
46747         var sl = this.view.getSelectedIndexes();
46748         var st = this.store;
46749         var nv = [];
46750         var tv = [];
46751         var r;
46752         Roo.each(sl, function(i) {
46753             r = st.getAt(i);
46754             nv.push(r.get(this.valueField));
46755         },this);
46756         this.setValue(Roo.encode(nv));
46757         if (this.value != this.valueBefore) {
46758
46759             this.fireEvent('change', this, this.value, this.valueBefore);
46760             this.valueBefore = this.value;
46761         }
46762         
46763     },
46764     
46765     setValue : function(v){
46766         // Roo.log(v);
46767         this.value = v;
46768         
46769         var vals = this.getValueArray();
46770         var tv = [];
46771         Roo.each(vals, function(k) {
46772             var r = this.findRecord(this.valueField, k);
46773             if(r){
46774                 tv.push(r.data[this.displayField]);
46775             }else if(this.valueNotFoundText !== undefined){
46776                 tv.push( this.valueNotFoundText );
46777             }
46778         },this);
46779        // Roo.log(tv);
46780         
46781         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46782         this.hiddenField.value = v;
46783         this.value = v;
46784     }
46785     
46786 });/*
46787  * Based on:
46788  * Ext JS Library 1.1.1
46789  * Copyright(c) 2006-2007, Ext JS, LLC.
46790  *
46791  * Originally Released Under LGPL - original licence link has changed is not relivant.
46792  *
46793  * Fork - LGPL
46794  * <script type="text/javascript">
46795  */
46796  
46797 /**
46798  * @class Roo.form.Signature
46799  * @extends Roo.form.Field
46800  * Signature field.  
46801  * @constructor
46802  * 
46803  * @param {Object} config Configuration options
46804  */
46805
46806 Roo.form.Signature = function(config){
46807     Roo.form.Signature.superclass.constructor.call(this, config);
46808     
46809     this.addEvents({// not in used??
46810          /**
46811          * @event confirm
46812          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46813              * @param {Roo.form.Signature} combo This combo box
46814              */
46815         'confirm' : true,
46816         /**
46817          * @event reset
46818          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46819              * @param {Roo.form.ComboBox} combo This combo box
46820              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46821              */
46822         'reset' : true
46823     });
46824 };
46825
46826 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46827     /**
46828      * @cfg {Object} labels Label to use when rendering a form.
46829      * defaults to 
46830      * labels : { 
46831      *      clear : "Clear",
46832      *      confirm : "Confirm"
46833      *  }
46834      */
46835     labels : { 
46836         clear : "Clear",
46837         confirm : "Confirm"
46838     },
46839     /**
46840      * @cfg {Number} width The signature panel width (defaults to 300)
46841      */
46842     width: 300,
46843     /**
46844      * @cfg {Number} height The signature panel height (defaults to 100)
46845      */
46846     height : 100,
46847     /**
46848      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46849      */
46850     allowBlank : false,
46851     
46852     //private
46853     // {Object} signPanel The signature SVG panel element (defaults to {})
46854     signPanel : {},
46855     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46856     isMouseDown : false,
46857     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46858     isConfirmed : false,
46859     // {String} signatureTmp SVG mapping string (defaults to empty string)
46860     signatureTmp : '',
46861     
46862     
46863     defaultAutoCreate : { // modified by initCompnoent..
46864         tag: "input",
46865         type:"hidden"
46866     },
46867
46868     // private
46869     onRender : function(ct, position){
46870         
46871         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46872         
46873         this.wrap = this.el.wrap({
46874             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46875         });
46876         
46877         this.createToolbar(this);
46878         this.signPanel = this.wrap.createChild({
46879                 tag: 'div',
46880                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46881             }, this.el
46882         );
46883             
46884         this.svgID = Roo.id();
46885         this.svgEl = this.signPanel.createChild({
46886               xmlns : 'http://www.w3.org/2000/svg',
46887               tag : 'svg',
46888               id : this.svgID + "-svg",
46889               width: this.width,
46890               height: this.height,
46891               viewBox: '0 0 '+this.width+' '+this.height,
46892               cn : [
46893                 {
46894                     tag: "rect",
46895                     id: this.svgID + "-svg-r",
46896                     width: this.width,
46897                     height: this.height,
46898                     fill: "#ffa"
46899                 },
46900                 {
46901                     tag: "line",
46902                     id: this.svgID + "-svg-l",
46903                     x1: "0", // start
46904                     y1: (this.height*0.8), // start set the line in 80% of height
46905                     x2: this.width, // end
46906                     y2: (this.height*0.8), // end set the line in 80% of height
46907                     'stroke': "#666",
46908                     'stroke-width': "1",
46909                     'stroke-dasharray': "3",
46910                     'shape-rendering': "crispEdges",
46911                     'pointer-events': "none"
46912                 },
46913                 {
46914                     tag: "path",
46915                     id: this.svgID + "-svg-p",
46916                     'stroke': "navy",
46917                     'stroke-width': "3",
46918                     'fill': "none",
46919                     'pointer-events': 'none'
46920                 }
46921               ]
46922         });
46923         this.createSVG();
46924         this.svgBox = this.svgEl.dom.getScreenCTM();
46925     },
46926     createSVG : function(){ 
46927         var svg = this.signPanel;
46928         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46929         var t = this;
46930
46931         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46932         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46933         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46934         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46935         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46936         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46937         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46938         
46939     },
46940     isTouchEvent : function(e){
46941         return e.type.match(/^touch/);
46942     },
46943     getCoords : function (e) {
46944         var pt    = this.svgEl.dom.createSVGPoint();
46945         pt.x = e.clientX; 
46946         pt.y = e.clientY;
46947         if (this.isTouchEvent(e)) {
46948             pt.x =  e.targetTouches[0].clientX 
46949             pt.y = e.targetTouches[0].clientY;
46950         }
46951         var a = this.svgEl.dom.getScreenCTM();
46952         var b = a.inverse();
46953         var mx = pt.matrixTransform(b);
46954         return mx.x + ',' + mx.y;
46955     },
46956     //mouse event headler 
46957     down : function (e) {
46958         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46959         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46960         
46961         this.isMouseDown = true;
46962         
46963         e.preventDefault();
46964     },
46965     move : function (e) {
46966         if (this.isMouseDown) {
46967             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46968             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46969         }
46970         
46971         e.preventDefault();
46972     },
46973     up : function (e) {
46974         this.isMouseDown = false;
46975         var sp = this.signatureTmp.split(' ');
46976         
46977         if(sp.length > 1){
46978             if(!sp[sp.length-2].match(/^L/)){
46979                 sp.pop();
46980                 sp.pop();
46981                 sp.push("");
46982                 this.signatureTmp = sp.join(" ");
46983             }
46984         }
46985         if(this.getValue() != this.signatureTmp){
46986             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46987             this.isConfirmed = false;
46988         }
46989         e.preventDefault();
46990     },
46991     
46992     /**
46993      * Protected method that will not generally be called directly. It
46994      * is called when the editor creates its toolbar. Override this method if you need to
46995      * add custom toolbar buttons.
46996      * @param {HtmlEditor} editor
46997      */
46998     createToolbar : function(editor){
46999          function btn(id, toggle, handler){
47000             var xid = fid + '-'+ id ;
47001             return {
47002                 id : xid,
47003                 cmd : id,
47004                 cls : 'x-btn-icon x-edit-'+id,
47005                 enableToggle:toggle !== false,
47006                 scope: editor, // was editor...
47007                 handler:handler||editor.relayBtnCmd,
47008                 clickEvent:'mousedown',
47009                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47010                 tabIndex:-1
47011             };
47012         }
47013         
47014         
47015         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47016         this.tb = tb;
47017         this.tb.add(
47018            {
47019                 cls : ' x-signature-btn x-signature-'+id,
47020                 scope: editor, // was editor...
47021                 handler: this.reset,
47022                 clickEvent:'mousedown',
47023                 text: this.labels.clear
47024             },
47025             {
47026                  xtype : 'Fill',
47027                  xns: Roo.Toolbar
47028             }, 
47029             {
47030                 cls : '  x-signature-btn x-signature-'+id,
47031                 scope: editor, // was editor...
47032                 handler: this.confirmHandler,
47033                 clickEvent:'mousedown',
47034                 text: this.labels.confirm
47035             }
47036         );
47037     
47038     },
47039     //public
47040     /**
47041      * when user is clicked confirm then show this image.....
47042      * 
47043      * @return {String} Image Data URI
47044      */
47045     getImageDataURI : function(){
47046         var svg = this.svgEl.dom.parentNode.innerHTML;
47047         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47048         return src; 
47049     },
47050     /**
47051      * 
47052      * @return {Boolean} this.isConfirmed
47053      */
47054     getConfirmed : function(){
47055         return this.isConfirmed;
47056     },
47057     /**
47058      * 
47059      * @return {Number} this.width
47060      */
47061     getWidth : function(){
47062         return this.width;
47063     },
47064     /**
47065      * 
47066      * @return {Number} this.height
47067      */
47068     getHeight : function(){
47069         return this.height;
47070     },
47071     // private
47072     getSignature : function(){
47073         return this.signatureTmp;
47074     },
47075     // private
47076     reset : function(){
47077         this.signatureTmp = '';
47078         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47079         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47080         this.isConfirmed = false;
47081         Roo.form.Signature.superclass.reset.call(this);
47082     },
47083     setSignature : function(s){
47084         this.signatureTmp = s;
47085         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47086         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47087         this.setValue(s);
47088         this.isConfirmed = false;
47089         Roo.form.Signature.superclass.reset.call(this);
47090     }, 
47091     test : function(){
47092 //        Roo.log(this.signPanel.dom.contentWindow.up())
47093     },
47094     //private
47095     setConfirmed : function(){
47096         
47097         
47098         
47099 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47100     },
47101     // private
47102     confirmHandler : function(){
47103         if(!this.getSignature()){
47104             return;
47105         }
47106         
47107         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47108         this.setValue(this.getSignature());
47109         this.isConfirmed = true;
47110         
47111         this.fireEvent('confirm', this);
47112     },
47113     // private
47114     // Subclasses should provide the validation implementation by overriding this
47115     validateValue : function(value){
47116         if(this.allowBlank){
47117             return true;
47118         }
47119         
47120         if(this.isConfirmed){
47121             return true;
47122         }
47123         return false;
47124     }
47125 });/*
47126  * Based on:
47127  * Ext JS Library 1.1.1
47128  * Copyright(c) 2006-2007, Ext JS, LLC.
47129  *
47130  * Originally Released Under LGPL - original licence link has changed is not relivant.
47131  *
47132  * Fork - LGPL
47133  * <script type="text/javascript">
47134  */
47135  
47136
47137 /**
47138  * @class Roo.form.ComboBox
47139  * @extends Roo.form.TriggerField
47140  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47141  * @constructor
47142  * Create a new ComboBox.
47143  * @param {Object} config Configuration options
47144  */
47145 Roo.form.Select = function(config){
47146     Roo.form.Select.superclass.constructor.call(this, config);
47147      
47148 };
47149
47150 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47151     /**
47152      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47153      */
47154     /**
47155      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47156      * rendering into an Roo.Editor, defaults to false)
47157      */
47158     /**
47159      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47160      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47161      */
47162     /**
47163      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47164      */
47165     /**
47166      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47167      * the dropdown list (defaults to undefined, with no header element)
47168      */
47169
47170      /**
47171      * @cfg {String/Roo.Template} tpl The template to use to render the output
47172      */
47173      
47174     // private
47175     defaultAutoCreate : {tag: "select"  },
47176     /**
47177      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47178      */
47179     listWidth: undefined,
47180     /**
47181      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47182      * mode = 'remote' or 'text' if mode = 'local')
47183      */
47184     displayField: undefined,
47185     /**
47186      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47187      * mode = 'remote' or 'value' if mode = 'local'). 
47188      * Note: use of a valueField requires the user make a selection
47189      * in order for a value to be mapped.
47190      */
47191     valueField: undefined,
47192     
47193     
47194     /**
47195      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47196      * field's data value (defaults to the underlying DOM element's name)
47197      */
47198     hiddenName: undefined,
47199     /**
47200      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47201      */
47202     listClass: '',
47203     /**
47204      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47205      */
47206     selectedClass: 'x-combo-selected',
47207     /**
47208      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47209      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47210      * which displays a downward arrow icon).
47211      */
47212     triggerClass : 'x-form-arrow-trigger',
47213     /**
47214      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47215      */
47216     shadow:'sides',
47217     /**
47218      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47219      * anchor positions (defaults to 'tl-bl')
47220      */
47221     listAlign: 'tl-bl?',
47222     /**
47223      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47224      */
47225     maxHeight: 300,
47226     /**
47227      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47228      * query specified by the allQuery config option (defaults to 'query')
47229      */
47230     triggerAction: 'query',
47231     /**
47232      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47233      * (defaults to 4, does not apply if editable = false)
47234      */
47235     minChars : 4,
47236     /**
47237      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47238      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47239      */
47240     typeAhead: false,
47241     /**
47242      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47243      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47244      */
47245     queryDelay: 500,
47246     /**
47247      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47248      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47249      */
47250     pageSize: 0,
47251     /**
47252      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47253      * when editable = true (defaults to false)
47254      */
47255     selectOnFocus:false,
47256     /**
47257      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47258      */
47259     queryParam: 'query',
47260     /**
47261      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47262      * when mode = 'remote' (defaults to 'Loading...')
47263      */
47264     loadingText: 'Loading...',
47265     /**
47266      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47267      */
47268     resizable: false,
47269     /**
47270      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47271      */
47272     handleHeight : 8,
47273     /**
47274      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47275      * traditional select (defaults to true)
47276      */
47277     editable: true,
47278     /**
47279      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47280      */
47281     allQuery: '',
47282     /**
47283      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47284      */
47285     mode: 'remote',
47286     /**
47287      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47288      * listWidth has a higher value)
47289      */
47290     minListWidth : 70,
47291     /**
47292      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47293      * allow the user to set arbitrary text into the field (defaults to false)
47294      */
47295     forceSelection:false,
47296     /**
47297      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47298      * if typeAhead = true (defaults to 250)
47299      */
47300     typeAheadDelay : 250,
47301     /**
47302      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47303      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47304      */
47305     valueNotFoundText : undefined,
47306     
47307     /**
47308      * @cfg {String} defaultValue The value displayed after loading the store.
47309      */
47310     defaultValue: '',
47311     
47312     /**
47313      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47314      */
47315     blockFocus : false,
47316     
47317     /**
47318      * @cfg {Boolean} disableClear Disable showing of clear button.
47319      */
47320     disableClear : false,
47321     /**
47322      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47323      */
47324     alwaysQuery : false,
47325     
47326     //private
47327     addicon : false,
47328     editicon: false,
47329     
47330     // element that contains real text value.. (when hidden is used..)
47331      
47332     // private
47333     onRender : function(ct, position){
47334         Roo.form.Field.prototype.onRender.call(this, ct, position);
47335         
47336         if(this.store){
47337             this.store.on('beforeload', this.onBeforeLoad, this);
47338             this.store.on('load', this.onLoad, this);
47339             this.store.on('loadexception', this.onLoadException, this);
47340             this.store.load({});
47341         }
47342         
47343         
47344         
47345     },
47346
47347     // private
47348     initEvents : function(){
47349         //Roo.form.ComboBox.superclass.initEvents.call(this);
47350  
47351     },
47352
47353     onDestroy : function(){
47354        
47355         if(this.store){
47356             this.store.un('beforeload', this.onBeforeLoad, this);
47357             this.store.un('load', this.onLoad, this);
47358             this.store.un('loadexception', this.onLoadException, this);
47359         }
47360         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47361     },
47362
47363     // private
47364     fireKey : function(e){
47365         if(e.isNavKeyPress() && !this.list.isVisible()){
47366             this.fireEvent("specialkey", this, e);
47367         }
47368     },
47369
47370     // private
47371     onResize: function(w, h){
47372         
47373         return; 
47374     
47375         
47376     },
47377
47378     /**
47379      * Allow or prevent the user from directly editing the field text.  If false is passed,
47380      * the user will only be able to select from the items defined in the dropdown list.  This method
47381      * is the runtime equivalent of setting the 'editable' config option at config time.
47382      * @param {Boolean} value True to allow the user to directly edit the field text
47383      */
47384     setEditable : function(value){
47385          
47386     },
47387
47388     // private
47389     onBeforeLoad : function(){
47390         
47391         Roo.log("Select before load");
47392         return;
47393     
47394         this.innerList.update(this.loadingText ?
47395                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47396         //this.restrictHeight();
47397         this.selectedIndex = -1;
47398     },
47399
47400     // private
47401     onLoad : function(){
47402
47403     
47404         var dom = this.el.dom;
47405         dom.innerHTML = '';
47406          var od = dom.ownerDocument;
47407          
47408         if (this.emptyText) {
47409             var op = od.createElement('option');
47410             op.setAttribute('value', '');
47411             op.innerHTML = String.format('{0}', this.emptyText);
47412             dom.appendChild(op);
47413         }
47414         if(this.store.getCount() > 0){
47415            
47416             var vf = this.valueField;
47417             var df = this.displayField;
47418             this.store.data.each(function(r) {
47419                 // which colmsn to use... testing - cdoe / title..
47420                 var op = od.createElement('option');
47421                 op.setAttribute('value', r.data[vf]);
47422                 op.innerHTML = String.format('{0}', r.data[df]);
47423                 dom.appendChild(op);
47424             });
47425             if (typeof(this.defaultValue != 'undefined')) {
47426                 this.setValue(this.defaultValue);
47427             }
47428             
47429              
47430         }else{
47431             //this.onEmptyResults();
47432         }
47433         //this.el.focus();
47434     },
47435     // private
47436     onLoadException : function()
47437     {
47438         dom.innerHTML = '';
47439             
47440         Roo.log("Select on load exception");
47441         return;
47442     
47443         this.collapse();
47444         Roo.log(this.store.reader.jsonData);
47445         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47446             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47447         }
47448         
47449         
47450     },
47451     // private
47452     onTypeAhead : function(){
47453          
47454     },
47455
47456     // private
47457     onSelect : function(record, index){
47458         Roo.log('on select?');
47459         return;
47460         if(this.fireEvent('beforeselect', this, record, index) !== false){
47461             this.setFromData(index > -1 ? record.data : false);
47462             this.collapse();
47463             this.fireEvent('select', this, record, index);
47464         }
47465     },
47466
47467     /**
47468      * Returns the currently selected field value or empty string if no value is set.
47469      * @return {String} value The selected value
47470      */
47471     getValue : function(){
47472         var dom = this.el.dom;
47473         this.value = dom.options[dom.selectedIndex].value;
47474         return this.value;
47475         
47476     },
47477
47478     /**
47479      * Clears any text/value currently set in the field
47480      */
47481     clearValue : function(){
47482         this.value = '';
47483         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47484         
47485     },
47486
47487     /**
47488      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47489      * will be displayed in the field.  If the value does not match the data value of an existing item,
47490      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47491      * Otherwise the field will be blank (although the value will still be set).
47492      * @param {String} value The value to match
47493      */
47494     setValue : function(v){
47495         var d = this.el.dom;
47496         for (var i =0; i < d.options.length;i++) {
47497             if (v == d.options[i].value) {
47498                 d.selectedIndex = i;
47499                 this.value = v;
47500                 return;
47501             }
47502         }
47503         this.clearValue();
47504     },
47505     /**
47506      * @property {Object} the last set data for the element
47507      */
47508     
47509     lastData : false,
47510     /**
47511      * Sets the value of the field based on a object which is related to the record format for the store.
47512      * @param {Object} value the value to set as. or false on reset?
47513      */
47514     setFromData : function(o){
47515         Roo.log('setfrom data?');
47516          
47517         
47518         
47519     },
47520     // private
47521     reset : function(){
47522         this.clearValue();
47523     },
47524     // private
47525     findRecord : function(prop, value){
47526         
47527         return false;
47528     
47529         var record;
47530         if(this.store.getCount() > 0){
47531             this.store.each(function(r){
47532                 if(r.data[prop] == value){
47533                     record = r;
47534                     return false;
47535                 }
47536                 return true;
47537             });
47538         }
47539         return record;
47540     },
47541     
47542     getName: function()
47543     {
47544         // returns hidden if it's set..
47545         if (!this.rendered) {return ''};
47546         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47547         
47548     },
47549      
47550
47551     
47552
47553     // private
47554     onEmptyResults : function(){
47555         Roo.log('empty results');
47556         //this.collapse();
47557     },
47558
47559     /**
47560      * Returns true if the dropdown list is expanded, else false.
47561      */
47562     isExpanded : function(){
47563         return false;
47564     },
47565
47566     /**
47567      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47568      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47569      * @param {String} value The data value of the item to select
47570      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47571      * selected item if it is not currently in view (defaults to true)
47572      * @return {Boolean} True if the value matched an item in the list, else false
47573      */
47574     selectByValue : function(v, scrollIntoView){
47575         Roo.log('select By Value');
47576         return false;
47577     
47578         if(v !== undefined && v !== null){
47579             var r = this.findRecord(this.valueField || this.displayField, v);
47580             if(r){
47581                 this.select(this.store.indexOf(r), scrollIntoView);
47582                 return true;
47583             }
47584         }
47585         return false;
47586     },
47587
47588     /**
47589      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47590      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47591      * @param {Number} index The zero-based index of the list item to select
47592      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47593      * selected item if it is not currently in view (defaults to true)
47594      */
47595     select : function(index, scrollIntoView){
47596         Roo.log('select ');
47597         return  ;
47598         
47599         this.selectedIndex = index;
47600         this.view.select(index);
47601         if(scrollIntoView !== false){
47602             var el = this.view.getNode(index);
47603             if(el){
47604                 this.innerList.scrollChildIntoView(el, false);
47605             }
47606         }
47607     },
47608
47609       
47610
47611     // private
47612     validateBlur : function(){
47613         
47614         return;
47615         
47616     },
47617
47618     // private
47619     initQuery : function(){
47620         this.doQuery(this.getRawValue());
47621     },
47622
47623     // private
47624     doForce : function(){
47625         if(this.el.dom.value.length > 0){
47626             this.el.dom.value =
47627                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47628              
47629         }
47630     },
47631
47632     /**
47633      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47634      * query allowing the query action to be canceled if needed.
47635      * @param {String} query The SQL query to execute
47636      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47637      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47638      * saved in the current store (defaults to false)
47639      */
47640     doQuery : function(q, forceAll){
47641         
47642         Roo.log('doQuery?');
47643         if(q === undefined || q === null){
47644             q = '';
47645         }
47646         var qe = {
47647             query: q,
47648             forceAll: forceAll,
47649             combo: this,
47650             cancel:false
47651         };
47652         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47653             return false;
47654         }
47655         q = qe.query;
47656         forceAll = qe.forceAll;
47657         if(forceAll === true || (q.length >= this.minChars)){
47658             if(this.lastQuery != q || this.alwaysQuery){
47659                 this.lastQuery = q;
47660                 if(this.mode == 'local'){
47661                     this.selectedIndex = -1;
47662                     if(forceAll){
47663                         this.store.clearFilter();
47664                     }else{
47665                         this.store.filter(this.displayField, q);
47666                     }
47667                     this.onLoad();
47668                 }else{
47669                     this.store.baseParams[this.queryParam] = q;
47670                     this.store.load({
47671                         params: this.getParams(q)
47672                     });
47673                     this.expand();
47674                 }
47675             }else{
47676                 this.selectedIndex = -1;
47677                 this.onLoad();   
47678             }
47679         }
47680     },
47681
47682     // private
47683     getParams : function(q){
47684         var p = {};
47685         //p[this.queryParam] = q;
47686         if(this.pageSize){
47687             p.start = 0;
47688             p.limit = this.pageSize;
47689         }
47690         return p;
47691     },
47692
47693     /**
47694      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47695      */
47696     collapse : function(){
47697         
47698     },
47699
47700     // private
47701     collapseIf : function(e){
47702         
47703     },
47704
47705     /**
47706      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47707      */
47708     expand : function(){
47709         
47710     } ,
47711
47712     // private
47713      
47714
47715     /** 
47716     * @cfg {Boolean} grow 
47717     * @hide 
47718     */
47719     /** 
47720     * @cfg {Number} growMin 
47721     * @hide 
47722     */
47723     /** 
47724     * @cfg {Number} growMax 
47725     * @hide 
47726     */
47727     /**
47728      * @hide
47729      * @method autoSize
47730      */
47731     
47732     setWidth : function()
47733     {
47734         
47735     },
47736     getResizeEl : function(){
47737         return this.el;
47738     }
47739 });//<script type="text/javasscript">
47740  
47741
47742 /**
47743  * @class Roo.DDView
47744  * A DnD enabled version of Roo.View.
47745  * @param {Element/String} container The Element in which to create the View.
47746  * @param {String} tpl The template string used to create the markup for each element of the View
47747  * @param {Object} config The configuration properties. These include all the config options of
47748  * {@link Roo.View} plus some specific to this class.<br>
47749  * <p>
47750  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47751  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47752  * <p>
47753  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47754 .x-view-drag-insert-above {
47755         border-top:1px dotted #3366cc;
47756 }
47757 .x-view-drag-insert-below {
47758         border-bottom:1px dotted #3366cc;
47759 }
47760 </code></pre>
47761  * 
47762  */
47763  
47764 Roo.DDView = function(container, tpl, config) {
47765     Roo.DDView.superclass.constructor.apply(this, arguments);
47766     this.getEl().setStyle("outline", "0px none");
47767     this.getEl().unselectable();
47768     if (this.dragGroup) {
47769                 this.setDraggable(this.dragGroup.split(","));
47770     }
47771     if (this.dropGroup) {
47772                 this.setDroppable(this.dropGroup.split(","));
47773     }
47774     if (this.deletable) {
47775         this.setDeletable();
47776     }
47777     this.isDirtyFlag = false;
47778         this.addEvents({
47779                 "drop" : true
47780         });
47781 };
47782
47783 Roo.extend(Roo.DDView, Roo.View, {
47784 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47785 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47786 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47787 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47788
47789         isFormField: true,
47790
47791         reset: Roo.emptyFn,
47792         
47793         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47794
47795         validate: function() {
47796                 return true;
47797         },
47798         
47799         destroy: function() {
47800                 this.purgeListeners();
47801                 this.getEl.removeAllListeners();
47802                 this.getEl().remove();
47803                 if (this.dragZone) {
47804                         if (this.dragZone.destroy) {
47805                                 this.dragZone.destroy();
47806                         }
47807                 }
47808                 if (this.dropZone) {
47809                         if (this.dropZone.destroy) {
47810                                 this.dropZone.destroy();
47811                         }
47812                 }
47813         },
47814
47815 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47816         getName: function() {
47817                 return this.name;
47818         },
47819
47820 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47821         setValue: function(v) {
47822                 if (!this.store) {
47823                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47824                 }
47825                 var data = {};
47826                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47827                 this.store.proxy = new Roo.data.MemoryProxy(data);
47828                 this.store.load();
47829         },
47830
47831 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47832         getValue: function() {
47833                 var result = '(';
47834                 this.store.each(function(rec) {
47835                         result += rec.id + ',';
47836                 });
47837                 return result.substr(0, result.length - 1) + ')';
47838         },
47839         
47840         getIds: function() {
47841                 var i = 0, result = new Array(this.store.getCount());
47842                 this.store.each(function(rec) {
47843                         result[i++] = rec.id;
47844                 });
47845                 return result;
47846         },
47847         
47848         isDirty: function() {
47849                 return this.isDirtyFlag;
47850         },
47851
47852 /**
47853  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47854  *      whole Element becomes the target, and this causes the drop gesture to append.
47855  */
47856     getTargetFromEvent : function(e) {
47857                 var target = e.getTarget();
47858                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47859                 target = target.parentNode;
47860                 }
47861                 if (!target) {
47862                         target = this.el.dom.lastChild || this.el.dom;
47863                 }
47864                 return target;
47865     },
47866
47867 /**
47868  *      Create the drag data which consists of an object which has the property "ddel" as
47869  *      the drag proxy element. 
47870  */
47871     getDragData : function(e) {
47872         var target = this.findItemFromChild(e.getTarget());
47873                 if(target) {
47874                         this.handleSelection(e);
47875                         var selNodes = this.getSelectedNodes();
47876             var dragData = {
47877                 source: this,
47878                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47879                 nodes: selNodes,
47880                 records: []
47881                         };
47882                         var selectedIndices = this.getSelectedIndexes();
47883                         for (var i = 0; i < selectedIndices.length; i++) {
47884                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47885                         }
47886                         if (selNodes.length == 1) {
47887                                 dragData.ddel = target.cloneNode(true); // the div element
47888                         } else {
47889                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47890                                 div.className = 'multi-proxy';
47891                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47892                                         div.appendChild(selNodes[i].cloneNode(true));
47893                                 }
47894                                 dragData.ddel = div;
47895                         }
47896             //console.log(dragData)
47897             //console.log(dragData.ddel.innerHTML)
47898                         return dragData;
47899                 }
47900         //console.log('nodragData')
47901                 return false;
47902     },
47903     
47904 /**     Specify to which ddGroup items in this DDView may be dragged. */
47905     setDraggable: function(ddGroup) {
47906         if (ddGroup instanceof Array) {
47907                 Roo.each(ddGroup, this.setDraggable, this);
47908                 return;
47909         }
47910         if (this.dragZone) {
47911                 this.dragZone.addToGroup(ddGroup);
47912         } else {
47913                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47914                                 containerScroll: true,
47915                                 ddGroup: ddGroup 
47916
47917                         });
47918 //                      Draggability implies selection. DragZone's mousedown selects the element.
47919                         if (!this.multiSelect) { this.singleSelect = true; }
47920
47921 //                      Wire the DragZone's handlers up to methods in *this*
47922                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47923                 }
47924     },
47925
47926 /**     Specify from which ddGroup this DDView accepts drops. */
47927     setDroppable: function(ddGroup) {
47928         if (ddGroup instanceof Array) {
47929                 Roo.each(ddGroup, this.setDroppable, this);
47930                 return;
47931         }
47932         if (this.dropZone) {
47933                 this.dropZone.addToGroup(ddGroup);
47934         } else {
47935                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47936                                 containerScroll: true,
47937                                 ddGroup: ddGroup
47938                         });
47939
47940 //                      Wire the DropZone's handlers up to methods in *this*
47941                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47942                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47943                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47944                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47945                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47946                 }
47947     },
47948
47949 /**     Decide whether to drop above or below a View node. */
47950     getDropPoint : function(e, n, dd){
47951         if (n == this.el.dom) { return "above"; }
47952                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47953                 var c = t + (b - t) / 2;
47954                 var y = Roo.lib.Event.getPageY(e);
47955                 if(y <= c) {
47956                         return "above";
47957                 }else{
47958                         return "below";
47959                 }
47960     },
47961
47962     onNodeEnter : function(n, dd, e, data){
47963                 return false;
47964     },
47965     
47966     onNodeOver : function(n, dd, e, data){
47967                 var pt = this.getDropPoint(e, n, dd);
47968                 // set the insert point style on the target node
47969                 var dragElClass = this.dropNotAllowed;
47970                 if (pt) {
47971                         var targetElClass;
47972                         if (pt == "above"){
47973                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47974                                 targetElClass = "x-view-drag-insert-above";
47975                         } else {
47976                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47977                                 targetElClass = "x-view-drag-insert-below";
47978                         }
47979                         if (this.lastInsertClass != targetElClass){
47980                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47981                                 this.lastInsertClass = targetElClass;
47982                         }
47983                 }
47984                 return dragElClass;
47985         },
47986
47987     onNodeOut : function(n, dd, e, data){
47988                 this.removeDropIndicators(n);
47989     },
47990
47991     onNodeDrop : function(n, dd, e, data){
47992         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47993                 return false;
47994         }
47995         var pt = this.getDropPoint(e, n, dd);
47996                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47997                 if (pt == "below") { insertAt++; }
47998                 for (var i = 0; i < data.records.length; i++) {
47999                         var r = data.records[i];
48000                         var dup = this.store.getById(r.id);
48001                         if (dup && (dd != this.dragZone)) {
48002                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48003                         } else {
48004                                 if (data.copy) {
48005                                         this.store.insert(insertAt++, r.copy());
48006                                 } else {
48007                                         data.source.isDirtyFlag = true;
48008                                         r.store.remove(r);
48009                                         this.store.insert(insertAt++, r);
48010                                 }
48011                                 this.isDirtyFlag = true;
48012                         }
48013                 }
48014                 this.dragZone.cachedTarget = null;
48015                 return true;
48016     },
48017
48018     removeDropIndicators : function(n){
48019                 if(n){
48020                         Roo.fly(n).removeClass([
48021                                 "x-view-drag-insert-above",
48022                                 "x-view-drag-insert-below"]);
48023                         this.lastInsertClass = "_noclass";
48024                 }
48025     },
48026
48027 /**
48028  *      Utility method. Add a delete option to the DDView's context menu.
48029  *      @param {String} imageUrl The URL of the "delete" icon image.
48030  */
48031         setDeletable: function(imageUrl) {
48032                 if (!this.singleSelect && !this.multiSelect) {
48033                         this.singleSelect = true;
48034                 }
48035                 var c = this.getContextMenu();
48036                 this.contextMenu.on("itemclick", function(item) {
48037                         switch (item.id) {
48038                                 case "delete":
48039                                         this.remove(this.getSelectedIndexes());
48040                                         break;
48041                         }
48042                 }, this);
48043                 this.contextMenu.add({
48044                         icon: imageUrl,
48045                         id: "delete",
48046                         text: 'Delete'
48047                 });
48048         },
48049         
48050 /**     Return the context menu for this DDView. */
48051         getContextMenu: function() {
48052                 if (!this.contextMenu) {
48053 //                      Create the View's context menu
48054                         this.contextMenu = new Roo.menu.Menu({
48055                                 id: this.id + "-contextmenu"
48056                         });
48057                         this.el.on("contextmenu", this.showContextMenu, this);
48058                 }
48059                 return this.contextMenu;
48060         },
48061         
48062         disableContextMenu: function() {
48063                 if (this.contextMenu) {
48064                         this.el.un("contextmenu", this.showContextMenu, this);
48065                 }
48066         },
48067
48068         showContextMenu: function(e, item) {
48069         item = this.findItemFromChild(e.getTarget());
48070                 if (item) {
48071                         e.stopEvent();
48072                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48073                         this.contextMenu.showAt(e.getXY());
48074             }
48075     },
48076
48077 /**
48078  *      Remove {@link Roo.data.Record}s at the specified indices.
48079  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48080  */
48081     remove: function(selectedIndices) {
48082                 selectedIndices = [].concat(selectedIndices);
48083                 for (var i = 0; i < selectedIndices.length; i++) {
48084                         var rec = this.store.getAt(selectedIndices[i]);
48085                         this.store.remove(rec);
48086                 }
48087     },
48088
48089 /**
48090  *      Double click fires the event, but also, if this is draggable, and there is only one other
48091  *      related DropZone, it transfers the selected node.
48092  */
48093     onDblClick : function(e){
48094         var item = this.findItemFromChild(e.getTarget());
48095         if(item){
48096             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48097                 return false;
48098             }
48099             if (this.dragGroup) {
48100                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48101                     while (targets.indexOf(this.dropZone) > -1) {
48102                             targets.remove(this.dropZone);
48103                                 }
48104                     if (targets.length == 1) {
48105                                         this.dragZone.cachedTarget = null;
48106                         var el = Roo.get(targets[0].getEl());
48107                         var box = el.getBox(true);
48108                         targets[0].onNodeDrop(el.dom, {
48109                                 target: el.dom,
48110                                 xy: [box.x, box.y + box.height - 1]
48111                         }, null, this.getDragData(e));
48112                     }
48113                 }
48114         }
48115     },
48116     
48117     handleSelection: function(e) {
48118                 this.dragZone.cachedTarget = null;
48119         var item = this.findItemFromChild(e.getTarget());
48120         if (!item) {
48121                 this.clearSelections(true);
48122                 return;
48123         }
48124                 if (item && (this.multiSelect || this.singleSelect)){
48125                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48126                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48127                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48128                                 this.unselect(item);
48129                         } else {
48130                                 this.select(item, this.multiSelect && e.ctrlKey);
48131                                 this.lastSelection = item;
48132                         }
48133                 }
48134     },
48135
48136     onItemClick : function(item, index, e){
48137                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48138                         return false;
48139                 }
48140                 return true;
48141     },
48142
48143     unselect : function(nodeInfo, suppressEvent){
48144                 var node = this.getNode(nodeInfo);
48145                 if(node && this.isSelected(node)){
48146                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48147                                 Roo.fly(node).removeClass(this.selectedClass);
48148                                 this.selections.remove(node);
48149                                 if(!suppressEvent){
48150                                         this.fireEvent("selectionchange", this, this.selections);
48151                                 }
48152                         }
48153                 }
48154     }
48155 });
48156 /*
48157  * Based on:
48158  * Ext JS Library 1.1.1
48159  * Copyright(c) 2006-2007, Ext JS, LLC.
48160  *
48161  * Originally Released Under LGPL - original licence link has changed is not relivant.
48162  *
48163  * Fork - LGPL
48164  * <script type="text/javascript">
48165  */
48166  
48167 /**
48168  * @class Roo.LayoutManager
48169  * @extends Roo.util.Observable
48170  * Base class for layout managers.
48171  */
48172 Roo.LayoutManager = function(container, config){
48173     Roo.LayoutManager.superclass.constructor.call(this);
48174     this.el = Roo.get(container);
48175     // ie scrollbar fix
48176     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48177         document.body.scroll = "no";
48178     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48179         this.el.position('relative');
48180     }
48181     this.id = this.el.id;
48182     this.el.addClass("x-layout-container");
48183     /** false to disable window resize monitoring @type Boolean */
48184     this.monitorWindowResize = true;
48185     this.regions = {};
48186     this.addEvents({
48187         /**
48188          * @event layout
48189          * Fires when a layout is performed. 
48190          * @param {Roo.LayoutManager} this
48191          */
48192         "layout" : true,
48193         /**
48194          * @event regionresized
48195          * Fires when the user resizes a region. 
48196          * @param {Roo.LayoutRegion} region The resized region
48197          * @param {Number} newSize The new size (width for east/west, height for north/south)
48198          */
48199         "regionresized" : true,
48200         /**
48201          * @event regioncollapsed
48202          * Fires when a region is collapsed. 
48203          * @param {Roo.LayoutRegion} region The collapsed region
48204          */
48205         "regioncollapsed" : true,
48206         /**
48207          * @event regionexpanded
48208          * Fires when a region is expanded.  
48209          * @param {Roo.LayoutRegion} region The expanded region
48210          */
48211         "regionexpanded" : true
48212     });
48213     this.updating = false;
48214     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48215 };
48216
48217 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48218     /**
48219      * Returns true if this layout is currently being updated
48220      * @return {Boolean}
48221      */
48222     isUpdating : function(){
48223         return this.updating; 
48224     },
48225     
48226     /**
48227      * Suspend the LayoutManager from doing auto-layouts while
48228      * making multiple add or remove calls
48229      */
48230     beginUpdate : function(){
48231         this.updating = true;    
48232     },
48233     
48234     /**
48235      * Restore auto-layouts and optionally disable the manager from performing a layout
48236      * @param {Boolean} noLayout true to disable a layout update 
48237      */
48238     endUpdate : function(noLayout){
48239         this.updating = false;
48240         if(!noLayout){
48241             this.layout();
48242         }    
48243     },
48244     
48245     layout: function(){
48246         
48247     },
48248     
48249     onRegionResized : function(region, newSize){
48250         this.fireEvent("regionresized", region, newSize);
48251         this.layout();
48252     },
48253     
48254     onRegionCollapsed : function(region){
48255         this.fireEvent("regioncollapsed", region);
48256     },
48257     
48258     onRegionExpanded : function(region){
48259         this.fireEvent("regionexpanded", region);
48260     },
48261         
48262     /**
48263      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48264      * performs box-model adjustments.
48265      * @return {Object} The size as an object {width: (the width), height: (the height)}
48266      */
48267     getViewSize : function(){
48268         var size;
48269         if(this.el.dom != document.body){
48270             size = this.el.getSize();
48271         }else{
48272             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48273         }
48274         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48275         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48276         return size;
48277     },
48278     
48279     /**
48280      * Returns the Element this layout is bound to.
48281      * @return {Roo.Element}
48282      */
48283     getEl : function(){
48284         return this.el;
48285     },
48286     
48287     /**
48288      * Returns the specified region.
48289      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48290      * @return {Roo.LayoutRegion}
48291      */
48292     getRegion : function(target){
48293         return this.regions[target.toLowerCase()];
48294     },
48295     
48296     onWindowResize : function(){
48297         if(this.monitorWindowResize){
48298             this.layout();
48299         }
48300     }
48301 });/*
48302  * Based on:
48303  * Ext JS Library 1.1.1
48304  * Copyright(c) 2006-2007, Ext JS, LLC.
48305  *
48306  * Originally Released Under LGPL - original licence link has changed is not relivant.
48307  *
48308  * Fork - LGPL
48309  * <script type="text/javascript">
48310  */
48311 /**
48312  * @class Roo.BorderLayout
48313  * @extends Roo.LayoutManager
48314  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48315  * please see: <br><br>
48316  * <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>
48317  * <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>
48318  * Example:
48319  <pre><code>
48320  var layout = new Roo.BorderLayout(document.body, {
48321     north: {
48322         initialSize: 25,
48323         titlebar: false
48324     },
48325     west: {
48326         split:true,
48327         initialSize: 200,
48328         minSize: 175,
48329         maxSize: 400,
48330         titlebar: true,
48331         collapsible: true
48332     },
48333     east: {
48334         split:true,
48335         initialSize: 202,
48336         minSize: 175,
48337         maxSize: 400,
48338         titlebar: true,
48339         collapsible: true
48340     },
48341     south: {
48342         split:true,
48343         initialSize: 100,
48344         minSize: 100,
48345         maxSize: 200,
48346         titlebar: true,
48347         collapsible: true
48348     },
48349     center: {
48350         titlebar: true,
48351         autoScroll:true,
48352         resizeTabs: true,
48353         minTabWidth: 50,
48354         preferredTabWidth: 150
48355     }
48356 });
48357
48358 // shorthand
48359 var CP = Roo.ContentPanel;
48360
48361 layout.beginUpdate();
48362 layout.add("north", new CP("north", "North"));
48363 layout.add("south", new CP("south", {title: "South", closable: true}));
48364 layout.add("west", new CP("west", {title: "West"}));
48365 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48366 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48367 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48368 layout.getRegion("center").showPanel("center1");
48369 layout.endUpdate();
48370 </code></pre>
48371
48372 <b>The container the layout is rendered into can be either the body element or any other element.
48373 If it is not the body element, the container needs to either be an absolute positioned element,
48374 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48375 the container size if it is not the body element.</b>
48376
48377 * @constructor
48378 * Create a new BorderLayout
48379 * @param {String/HTMLElement/Element} container The container this layout is bound to
48380 * @param {Object} config Configuration options
48381  */
48382 Roo.BorderLayout = function(container, config){
48383     config = config || {};
48384     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48385     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48386     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48387         var target = this.factory.validRegions[i];
48388         if(config[target]){
48389             this.addRegion(target, config[target]);
48390         }
48391     }
48392 };
48393
48394 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48395     /**
48396      * Creates and adds a new region if it doesn't already exist.
48397      * @param {String} target The target region key (north, south, east, west or center).
48398      * @param {Object} config The regions config object
48399      * @return {BorderLayoutRegion} The new region
48400      */
48401     addRegion : function(target, config){
48402         if(!this.regions[target]){
48403             var r = this.factory.create(target, this, config);
48404             this.bindRegion(target, r);
48405         }
48406         return this.regions[target];
48407     },
48408
48409     // private (kinda)
48410     bindRegion : function(name, r){
48411         this.regions[name] = r;
48412         r.on("visibilitychange", this.layout, this);
48413         r.on("paneladded", this.layout, this);
48414         r.on("panelremoved", this.layout, this);
48415         r.on("invalidated", this.layout, this);
48416         r.on("resized", this.onRegionResized, this);
48417         r.on("collapsed", this.onRegionCollapsed, this);
48418         r.on("expanded", this.onRegionExpanded, this);
48419     },
48420
48421     /**
48422      * Performs a layout update.
48423      */
48424     layout : function(){
48425         if(this.updating) return;
48426         var size = this.getViewSize();
48427         var w = size.width;
48428         var h = size.height;
48429         var centerW = w;
48430         var centerH = h;
48431         var centerY = 0;
48432         var centerX = 0;
48433         //var x = 0, y = 0;
48434
48435         var rs = this.regions;
48436         var north = rs["north"];
48437         var south = rs["south"]; 
48438         var west = rs["west"];
48439         var east = rs["east"];
48440         var center = rs["center"];
48441         //if(this.hideOnLayout){ // not supported anymore
48442             //c.el.setStyle("display", "none");
48443         //}
48444         if(north && north.isVisible()){
48445             var b = north.getBox();
48446             var m = north.getMargins();
48447             b.width = w - (m.left+m.right);
48448             b.x = m.left;
48449             b.y = m.top;
48450             centerY = b.height + b.y + m.bottom;
48451             centerH -= centerY;
48452             north.updateBox(this.safeBox(b));
48453         }
48454         if(south && south.isVisible()){
48455             var b = south.getBox();
48456             var m = south.getMargins();
48457             b.width = w - (m.left+m.right);
48458             b.x = m.left;
48459             var totalHeight = (b.height + m.top + m.bottom);
48460             b.y = h - totalHeight + m.top;
48461             centerH -= totalHeight;
48462             south.updateBox(this.safeBox(b));
48463         }
48464         if(west && west.isVisible()){
48465             var b = west.getBox();
48466             var m = west.getMargins();
48467             b.height = centerH - (m.top+m.bottom);
48468             b.x = m.left;
48469             b.y = centerY + m.top;
48470             var totalWidth = (b.width + m.left + m.right);
48471             centerX += totalWidth;
48472             centerW -= totalWidth;
48473             west.updateBox(this.safeBox(b));
48474         }
48475         if(east && east.isVisible()){
48476             var b = east.getBox();
48477             var m = east.getMargins();
48478             b.height = centerH - (m.top+m.bottom);
48479             var totalWidth = (b.width + m.left + m.right);
48480             b.x = w - totalWidth + m.left;
48481             b.y = centerY + m.top;
48482             centerW -= totalWidth;
48483             east.updateBox(this.safeBox(b));
48484         }
48485         if(center){
48486             var m = center.getMargins();
48487             var centerBox = {
48488                 x: centerX + m.left,
48489                 y: centerY + m.top,
48490                 width: centerW - (m.left+m.right),
48491                 height: centerH - (m.top+m.bottom)
48492             };
48493             //if(this.hideOnLayout){
48494                 //center.el.setStyle("display", "block");
48495             //}
48496             center.updateBox(this.safeBox(centerBox));
48497         }
48498         this.el.repaint();
48499         this.fireEvent("layout", this);
48500     },
48501
48502     // private
48503     safeBox : function(box){
48504         box.width = Math.max(0, box.width);
48505         box.height = Math.max(0, box.height);
48506         return box;
48507     },
48508
48509     /**
48510      * Adds a ContentPanel (or subclass) to this layout.
48511      * @param {String} target The target region key (north, south, east, west or center).
48512      * @param {Roo.ContentPanel} panel The panel to add
48513      * @return {Roo.ContentPanel} The added panel
48514      */
48515     add : function(target, panel){
48516          
48517         target = target.toLowerCase();
48518         return this.regions[target].add(panel);
48519     },
48520
48521     /**
48522      * Remove a ContentPanel (or subclass) to this layout.
48523      * @param {String} target The target region key (north, south, east, west or center).
48524      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48525      * @return {Roo.ContentPanel} The removed panel
48526      */
48527     remove : function(target, panel){
48528         target = target.toLowerCase();
48529         return this.regions[target].remove(panel);
48530     },
48531
48532     /**
48533      * Searches all regions for a panel with the specified id
48534      * @param {String} panelId
48535      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48536      */
48537     findPanel : function(panelId){
48538         var rs = this.regions;
48539         for(var target in rs){
48540             if(typeof rs[target] != "function"){
48541                 var p = rs[target].getPanel(panelId);
48542                 if(p){
48543                     return p;
48544                 }
48545             }
48546         }
48547         return null;
48548     },
48549
48550     /**
48551      * Searches all regions for a panel with the specified id and activates (shows) it.
48552      * @param {String/ContentPanel} panelId The panels id or the panel itself
48553      * @return {Roo.ContentPanel} The shown panel or null
48554      */
48555     showPanel : function(panelId) {
48556       var rs = this.regions;
48557       for(var target in rs){
48558          var r = rs[target];
48559          if(typeof r != "function"){
48560             if(r.hasPanel(panelId)){
48561                return r.showPanel(panelId);
48562             }
48563          }
48564       }
48565       return null;
48566    },
48567
48568    /**
48569      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48570      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48571      */
48572     restoreState : function(provider){
48573         if(!provider){
48574             provider = Roo.state.Manager;
48575         }
48576         var sm = new Roo.LayoutStateManager();
48577         sm.init(this, provider);
48578     },
48579
48580     /**
48581      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48582      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48583      * a valid ContentPanel config object.  Example:
48584      * <pre><code>
48585 // Create the main layout
48586 var layout = new Roo.BorderLayout('main-ct', {
48587     west: {
48588         split:true,
48589         minSize: 175,
48590         titlebar: true
48591     },
48592     center: {
48593         title:'Components'
48594     }
48595 }, 'main-ct');
48596
48597 // Create and add multiple ContentPanels at once via configs
48598 layout.batchAdd({
48599    west: {
48600        id: 'source-files',
48601        autoCreate:true,
48602        title:'Ext Source Files',
48603        autoScroll:true,
48604        fitToFrame:true
48605    },
48606    center : {
48607        el: cview,
48608        autoScroll:true,
48609        fitToFrame:true,
48610        toolbar: tb,
48611        resizeEl:'cbody'
48612    }
48613 });
48614 </code></pre>
48615      * @param {Object} regions An object containing ContentPanel configs by region name
48616      */
48617     batchAdd : function(regions){
48618         this.beginUpdate();
48619         for(var rname in regions){
48620             var lr = this.regions[rname];
48621             if(lr){
48622                 this.addTypedPanels(lr, regions[rname]);
48623             }
48624         }
48625         this.endUpdate();
48626     },
48627
48628     // private
48629     addTypedPanels : function(lr, ps){
48630         if(typeof ps == 'string'){
48631             lr.add(new Roo.ContentPanel(ps));
48632         }
48633         else if(ps instanceof Array){
48634             for(var i =0, len = ps.length; i < len; i++){
48635                 this.addTypedPanels(lr, ps[i]);
48636             }
48637         }
48638         else if(!ps.events){ // raw config?
48639             var el = ps.el;
48640             delete ps.el; // prevent conflict
48641             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48642         }
48643         else {  // panel object assumed!
48644             lr.add(ps);
48645         }
48646     },
48647     /**
48648      * Adds a xtype elements to the layout.
48649      * <pre><code>
48650
48651 layout.addxtype({
48652        xtype : 'ContentPanel',
48653        region: 'west',
48654        items: [ .... ]
48655    }
48656 );
48657
48658 layout.addxtype({
48659         xtype : 'NestedLayoutPanel',
48660         region: 'west',
48661         layout: {
48662            center: { },
48663            west: { }   
48664         },
48665         items : [ ... list of content panels or nested layout panels.. ]
48666    }
48667 );
48668 </code></pre>
48669      * @param {Object} cfg Xtype definition of item to add.
48670      */
48671     addxtype : function(cfg)
48672     {
48673         // basically accepts a pannel...
48674         // can accept a layout region..!?!?
48675         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48676         
48677         if (!cfg.xtype.match(/Panel$/)) {
48678             return false;
48679         }
48680         var ret = false;
48681         
48682         if (typeof(cfg.region) == 'undefined') {
48683             Roo.log("Failed to add Panel, region was not set");
48684             Roo.log(cfg);
48685             return false;
48686         }
48687         var region = cfg.region;
48688         delete cfg.region;
48689         
48690           
48691         var xitems = [];
48692         if (cfg.items) {
48693             xitems = cfg.items;
48694             delete cfg.items;
48695         }
48696         var nb = false;
48697         
48698         switch(cfg.xtype) 
48699         {
48700             case 'ContentPanel':  // ContentPanel (el, cfg)
48701             case 'ScrollPanel':  // ContentPanel (el, cfg)
48702             case 'ViewPanel': 
48703                 if(cfg.autoCreate) {
48704                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48705                 } else {
48706                     var el = this.el.createChild();
48707                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48708                 }
48709                 
48710                 this.add(region, ret);
48711                 break;
48712             
48713             
48714             case 'TreePanel': // our new panel!
48715                 cfg.el = this.el.createChild();
48716                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48717                 this.add(region, ret);
48718                 break;
48719             
48720             case 'NestedLayoutPanel': 
48721                 // create a new Layout (which is  a Border Layout...
48722                 var el = this.el.createChild();
48723                 var clayout = cfg.layout;
48724                 delete cfg.layout;
48725                 clayout.items   = clayout.items  || [];
48726                 // replace this exitems with the clayout ones..
48727                 xitems = clayout.items;
48728                  
48729                 
48730                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48731                     cfg.background = false;
48732                 }
48733                 var layout = new Roo.BorderLayout(el, clayout);
48734                 
48735                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48736                 //console.log('adding nested layout panel '  + cfg.toSource());
48737                 this.add(region, ret);
48738                 nb = {}; /// find first...
48739                 break;
48740                 
48741             case 'GridPanel': 
48742             
48743                 // needs grid and region
48744                 
48745                 //var el = this.getRegion(region).el.createChild();
48746                 var el = this.el.createChild();
48747                 // create the grid first...
48748                 
48749                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48750                 delete cfg.grid;
48751                 if (region == 'center' && this.active ) {
48752                     cfg.background = false;
48753                 }
48754                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48755                 
48756                 this.add(region, ret);
48757                 if (cfg.background) {
48758                     ret.on('activate', function(gp) {
48759                         if (!gp.grid.rendered) {
48760                             gp.grid.render();
48761                         }
48762                     });
48763                 } else {
48764                     grid.render();
48765                 }
48766                 break;
48767            
48768            
48769            
48770                 
48771                 
48772                 
48773             default:
48774                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
48775                     
48776                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48777                     this.add(region, ret);
48778                 } else {
48779                 
48780                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48781                     return null;
48782                 }
48783                 
48784              // GridPanel (grid, cfg)
48785             
48786         }
48787         this.beginUpdate();
48788         // add children..
48789         var region = '';
48790         var abn = {};
48791         Roo.each(xitems, function(i)  {
48792             region = nb && i.region ? i.region : false;
48793             
48794             var add = ret.addxtype(i);
48795            
48796             if (region) {
48797                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48798                 if (!i.background) {
48799                     abn[region] = nb[region] ;
48800                 }
48801             }
48802             
48803         });
48804         this.endUpdate();
48805
48806         // make the last non-background panel active..
48807         //if (nb) { Roo.log(abn); }
48808         if (nb) {
48809             
48810             for(var r in abn) {
48811                 region = this.getRegion(r);
48812                 if (region) {
48813                     // tried using nb[r], but it does not work..
48814                      
48815                     region.showPanel(abn[r]);
48816                    
48817                 }
48818             }
48819         }
48820         return ret;
48821         
48822     }
48823 });
48824
48825 /**
48826  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48827  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48828  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48829  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48830  * <pre><code>
48831 // shorthand
48832 var CP = Roo.ContentPanel;
48833
48834 var layout = Roo.BorderLayout.create({
48835     north: {
48836         initialSize: 25,
48837         titlebar: false,
48838         panels: [new CP("north", "North")]
48839     },
48840     west: {
48841         split:true,
48842         initialSize: 200,
48843         minSize: 175,
48844         maxSize: 400,
48845         titlebar: true,
48846         collapsible: true,
48847         panels: [new CP("west", {title: "West"})]
48848     },
48849     east: {
48850         split:true,
48851         initialSize: 202,
48852         minSize: 175,
48853         maxSize: 400,
48854         titlebar: true,
48855         collapsible: true,
48856         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48857     },
48858     south: {
48859         split:true,
48860         initialSize: 100,
48861         minSize: 100,
48862         maxSize: 200,
48863         titlebar: true,
48864         collapsible: true,
48865         panels: [new CP("south", {title: "South", closable: true})]
48866     },
48867     center: {
48868         titlebar: true,
48869         autoScroll:true,
48870         resizeTabs: true,
48871         minTabWidth: 50,
48872         preferredTabWidth: 150,
48873         panels: [
48874             new CP("center1", {title: "Close Me", closable: true}),
48875             new CP("center2", {title: "Center Panel", closable: false})
48876         ]
48877     }
48878 }, document.body);
48879
48880 layout.getRegion("center").showPanel("center1");
48881 </code></pre>
48882  * @param config
48883  * @param targetEl
48884  */
48885 Roo.BorderLayout.create = function(config, targetEl){
48886     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48887     layout.beginUpdate();
48888     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48889     for(var j = 0, jlen = regions.length; j < jlen; j++){
48890         var lr = regions[j];
48891         if(layout.regions[lr] && config[lr].panels){
48892             var r = layout.regions[lr];
48893             var ps = config[lr].panels;
48894             layout.addTypedPanels(r, ps);
48895         }
48896     }
48897     layout.endUpdate();
48898     return layout;
48899 };
48900
48901 // private
48902 Roo.BorderLayout.RegionFactory = {
48903     // private
48904     validRegions : ["north","south","east","west","center"],
48905
48906     // private
48907     create : function(target, mgr, config){
48908         target = target.toLowerCase();
48909         if(config.lightweight || config.basic){
48910             return new Roo.BasicLayoutRegion(mgr, config, target);
48911         }
48912         switch(target){
48913             case "north":
48914                 return new Roo.NorthLayoutRegion(mgr, config);
48915             case "south":
48916                 return new Roo.SouthLayoutRegion(mgr, config);
48917             case "east":
48918                 return new Roo.EastLayoutRegion(mgr, config);
48919             case "west":
48920                 return new Roo.WestLayoutRegion(mgr, config);
48921             case "center":
48922                 return new Roo.CenterLayoutRegion(mgr, config);
48923         }
48924         throw 'Layout region "'+target+'" not supported.';
48925     }
48926 };/*
48927  * Based on:
48928  * Ext JS Library 1.1.1
48929  * Copyright(c) 2006-2007, Ext JS, LLC.
48930  *
48931  * Originally Released Under LGPL - original licence link has changed is not relivant.
48932  *
48933  * Fork - LGPL
48934  * <script type="text/javascript">
48935  */
48936  
48937 /**
48938  * @class Roo.BasicLayoutRegion
48939  * @extends Roo.util.Observable
48940  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48941  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48942  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48943  */
48944 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48945     this.mgr = mgr;
48946     this.position  = pos;
48947     this.events = {
48948         /**
48949          * @scope Roo.BasicLayoutRegion
48950          */
48951         
48952         /**
48953          * @event beforeremove
48954          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48955          * @param {Roo.LayoutRegion} this
48956          * @param {Roo.ContentPanel} panel The panel
48957          * @param {Object} e The cancel event object
48958          */
48959         "beforeremove" : true,
48960         /**
48961          * @event invalidated
48962          * Fires when the layout for this region is changed.
48963          * @param {Roo.LayoutRegion} this
48964          */
48965         "invalidated" : true,
48966         /**
48967          * @event visibilitychange
48968          * Fires when this region is shown or hidden 
48969          * @param {Roo.LayoutRegion} this
48970          * @param {Boolean} visibility true or false
48971          */
48972         "visibilitychange" : true,
48973         /**
48974          * @event paneladded
48975          * Fires when a panel is added. 
48976          * @param {Roo.LayoutRegion} this
48977          * @param {Roo.ContentPanel} panel The panel
48978          */
48979         "paneladded" : true,
48980         /**
48981          * @event panelremoved
48982          * Fires when a panel is removed. 
48983          * @param {Roo.LayoutRegion} this
48984          * @param {Roo.ContentPanel} panel The panel
48985          */
48986         "panelremoved" : true,
48987         /**
48988          * @event collapsed
48989          * Fires when this region is collapsed.
48990          * @param {Roo.LayoutRegion} this
48991          */
48992         "collapsed" : true,
48993         /**
48994          * @event expanded
48995          * Fires when this region is expanded.
48996          * @param {Roo.LayoutRegion} this
48997          */
48998         "expanded" : true,
48999         /**
49000          * @event slideshow
49001          * Fires when this region is slid into view.
49002          * @param {Roo.LayoutRegion} this
49003          */
49004         "slideshow" : true,
49005         /**
49006          * @event slidehide
49007          * Fires when this region slides out of view. 
49008          * @param {Roo.LayoutRegion} this
49009          */
49010         "slidehide" : true,
49011         /**
49012          * @event panelactivated
49013          * Fires when a panel is activated. 
49014          * @param {Roo.LayoutRegion} this
49015          * @param {Roo.ContentPanel} panel The activated panel
49016          */
49017         "panelactivated" : true,
49018         /**
49019          * @event resized
49020          * Fires when the user resizes this region. 
49021          * @param {Roo.LayoutRegion} this
49022          * @param {Number} newSize The new size (width for east/west, height for north/south)
49023          */
49024         "resized" : true
49025     };
49026     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49027     this.panels = new Roo.util.MixedCollection();
49028     this.panels.getKey = this.getPanelId.createDelegate(this);
49029     this.box = null;
49030     this.activePanel = null;
49031     // ensure listeners are added...
49032     
49033     if (config.listeners || config.events) {
49034         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49035             listeners : config.listeners || {},
49036             events : config.events || {}
49037         });
49038     }
49039     
49040     if(skipConfig !== true){
49041         this.applyConfig(config);
49042     }
49043 };
49044
49045 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49046     getPanelId : function(p){
49047         return p.getId();
49048     },
49049     
49050     applyConfig : function(config){
49051         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49052         this.config = config;
49053         
49054     },
49055     
49056     /**
49057      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49058      * the width, for horizontal (north, south) the height.
49059      * @param {Number} newSize The new width or height
49060      */
49061     resizeTo : function(newSize){
49062         var el = this.el ? this.el :
49063                  (this.activePanel ? this.activePanel.getEl() : null);
49064         if(el){
49065             switch(this.position){
49066                 case "east":
49067                 case "west":
49068                     el.setWidth(newSize);
49069                     this.fireEvent("resized", this, newSize);
49070                 break;
49071                 case "north":
49072                 case "south":
49073                     el.setHeight(newSize);
49074                     this.fireEvent("resized", this, newSize);
49075                 break;                
49076             }
49077         }
49078     },
49079     
49080     getBox : function(){
49081         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49082     },
49083     
49084     getMargins : function(){
49085         return this.margins;
49086     },
49087     
49088     updateBox : function(box){
49089         this.box = box;
49090         var el = this.activePanel.getEl();
49091         el.dom.style.left = box.x + "px";
49092         el.dom.style.top = box.y + "px";
49093         this.activePanel.setSize(box.width, box.height);
49094     },
49095     
49096     /**
49097      * Returns the container element for this region.
49098      * @return {Roo.Element}
49099      */
49100     getEl : function(){
49101         return this.activePanel;
49102     },
49103     
49104     /**
49105      * Returns true if this region is currently visible.
49106      * @return {Boolean}
49107      */
49108     isVisible : function(){
49109         return this.activePanel ? true : false;
49110     },
49111     
49112     setActivePanel : function(panel){
49113         panel = this.getPanel(panel);
49114         if(this.activePanel && this.activePanel != panel){
49115             this.activePanel.setActiveState(false);
49116             this.activePanel.getEl().setLeftTop(-10000,-10000);
49117         }
49118         this.activePanel = panel;
49119         panel.setActiveState(true);
49120         if(this.box){
49121             panel.setSize(this.box.width, this.box.height);
49122         }
49123         this.fireEvent("panelactivated", this, panel);
49124         this.fireEvent("invalidated");
49125     },
49126     
49127     /**
49128      * Show the specified panel.
49129      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49130      * @return {Roo.ContentPanel} The shown panel or null
49131      */
49132     showPanel : function(panel){
49133         if(panel = this.getPanel(panel)){
49134             this.setActivePanel(panel);
49135         }
49136         return panel;
49137     },
49138     
49139     /**
49140      * Get the active panel for this region.
49141      * @return {Roo.ContentPanel} The active panel or null
49142      */
49143     getActivePanel : function(){
49144         return this.activePanel;
49145     },
49146     
49147     /**
49148      * Add the passed ContentPanel(s)
49149      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49150      * @return {Roo.ContentPanel} The panel added (if only one was added)
49151      */
49152     add : function(panel){
49153         if(arguments.length > 1){
49154             for(var i = 0, len = arguments.length; i < len; i++) {
49155                 this.add(arguments[i]);
49156             }
49157             return null;
49158         }
49159         if(this.hasPanel(panel)){
49160             this.showPanel(panel);
49161             return panel;
49162         }
49163         var el = panel.getEl();
49164         if(el.dom.parentNode != this.mgr.el.dom){
49165             this.mgr.el.dom.appendChild(el.dom);
49166         }
49167         if(panel.setRegion){
49168             panel.setRegion(this);
49169         }
49170         this.panels.add(panel);
49171         el.setStyle("position", "absolute");
49172         if(!panel.background){
49173             this.setActivePanel(panel);
49174             if(this.config.initialSize && this.panels.getCount()==1){
49175                 this.resizeTo(this.config.initialSize);
49176             }
49177         }
49178         this.fireEvent("paneladded", this, panel);
49179         return panel;
49180     },
49181     
49182     /**
49183      * Returns true if the panel is in this region.
49184      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49185      * @return {Boolean}
49186      */
49187     hasPanel : function(panel){
49188         if(typeof panel == "object"){ // must be panel obj
49189             panel = panel.getId();
49190         }
49191         return this.getPanel(panel) ? true : false;
49192     },
49193     
49194     /**
49195      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49196      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49197      * @param {Boolean} preservePanel Overrides the config preservePanel option
49198      * @return {Roo.ContentPanel} The panel that was removed
49199      */
49200     remove : function(panel, preservePanel){
49201         panel = this.getPanel(panel);
49202         if(!panel){
49203             return null;
49204         }
49205         var e = {};
49206         this.fireEvent("beforeremove", this, panel, e);
49207         if(e.cancel === true){
49208             return null;
49209         }
49210         var panelId = panel.getId();
49211         this.panels.removeKey(panelId);
49212         return panel;
49213     },
49214     
49215     /**
49216      * Returns the panel specified or null if it's not in this region.
49217      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49218      * @return {Roo.ContentPanel}
49219      */
49220     getPanel : function(id){
49221         if(typeof id == "object"){ // must be panel obj
49222             return id;
49223         }
49224         return this.panels.get(id);
49225     },
49226     
49227     /**
49228      * Returns this regions position (north/south/east/west/center).
49229      * @return {String} 
49230      */
49231     getPosition: function(){
49232         return this.position;    
49233     }
49234 });/*
49235  * Based on:
49236  * Ext JS Library 1.1.1
49237  * Copyright(c) 2006-2007, Ext JS, LLC.
49238  *
49239  * Originally Released Under LGPL - original licence link has changed is not relivant.
49240  *
49241  * Fork - LGPL
49242  * <script type="text/javascript">
49243  */
49244  
49245 /**
49246  * @class Roo.LayoutRegion
49247  * @extends Roo.BasicLayoutRegion
49248  * This class represents a region in a layout manager.
49249  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49250  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49251  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49252  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49253  * @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})
49254  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49255  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49256  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49257  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49258  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49259  * @cfg {String}    title           The title for the region (overrides panel titles)
49260  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49261  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49262  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49263  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49264  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49265  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49266  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49267  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49268  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49269  * @cfg {Boolean}   showPin         True to show a pin button
49270  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49271  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49272  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49273  * @cfg {Number}    width           For East/West panels
49274  * @cfg {Number}    height          For North/South panels
49275  * @cfg {Boolean}   split           To show the splitter
49276  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49277  */
49278 Roo.LayoutRegion = function(mgr, config, pos){
49279     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49280     var dh = Roo.DomHelper;
49281     /** This region's container element 
49282     * @type Roo.Element */
49283     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49284     /** This region's title element 
49285     * @type Roo.Element */
49286
49287     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49288         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49289         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49290     ]}, true);
49291     this.titleEl.enableDisplayMode();
49292     /** This region's title text element 
49293     * @type HTMLElement */
49294     this.titleTextEl = this.titleEl.dom.firstChild;
49295     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49296     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49297     this.closeBtn.enableDisplayMode();
49298     this.closeBtn.on("click", this.closeClicked, this);
49299     this.closeBtn.hide();
49300
49301     this.createBody(config);
49302     this.visible = true;
49303     this.collapsed = false;
49304
49305     if(config.hideWhenEmpty){
49306         this.hide();
49307         this.on("paneladded", this.validateVisibility, this);
49308         this.on("panelremoved", this.validateVisibility, this);
49309     }
49310     this.applyConfig(config);
49311 };
49312
49313 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49314
49315     createBody : function(){
49316         /** This region's body element 
49317         * @type Roo.Element */
49318         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49319     },
49320
49321     applyConfig : function(c){
49322         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49323             var dh = Roo.DomHelper;
49324             if(c.titlebar !== false){
49325                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49326                 this.collapseBtn.on("click", this.collapse, this);
49327                 this.collapseBtn.enableDisplayMode();
49328
49329                 if(c.showPin === true || this.showPin){
49330                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49331                     this.stickBtn.enableDisplayMode();
49332                     this.stickBtn.on("click", this.expand, this);
49333                     this.stickBtn.hide();
49334                 }
49335             }
49336             /** This region's collapsed element
49337             * @type Roo.Element */
49338             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49339                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49340             ]}, true);
49341             if(c.floatable !== false){
49342                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49343                this.collapsedEl.on("click", this.collapseClick, this);
49344             }
49345
49346             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49347                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49348                    id: "message", unselectable: "on", style:{"float":"left"}});
49349                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49350              }
49351             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49352             this.expandBtn.on("click", this.expand, this);
49353         }
49354         if(this.collapseBtn){
49355             this.collapseBtn.setVisible(c.collapsible == true);
49356         }
49357         this.cmargins = c.cmargins || this.cmargins ||
49358                          (this.position == "west" || this.position == "east" ?
49359                              {top: 0, left: 2, right:2, bottom: 0} :
49360                              {top: 2, left: 0, right:0, bottom: 2});
49361         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49362         this.bottomTabs = c.tabPosition != "top";
49363         this.autoScroll = c.autoScroll || false;
49364         if(this.autoScroll){
49365             this.bodyEl.setStyle("overflow", "auto");
49366         }else{
49367             this.bodyEl.setStyle("overflow", "hidden");
49368         }
49369         //if(c.titlebar !== false){
49370             if((!c.titlebar && !c.title) || c.titlebar === false){
49371                 this.titleEl.hide();
49372             }else{
49373                 this.titleEl.show();
49374                 if(c.title){
49375                     this.titleTextEl.innerHTML = c.title;
49376                 }
49377             }
49378         //}
49379         this.duration = c.duration || .30;
49380         this.slideDuration = c.slideDuration || .45;
49381         this.config = c;
49382         if(c.collapsed){
49383             this.collapse(true);
49384         }
49385         if(c.hidden){
49386             this.hide();
49387         }
49388     },
49389     /**
49390      * Returns true if this region is currently visible.
49391      * @return {Boolean}
49392      */
49393     isVisible : function(){
49394         return this.visible;
49395     },
49396
49397     /**
49398      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49399      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49400      */
49401     setCollapsedTitle : function(title){
49402         title = title || "&#160;";
49403         if(this.collapsedTitleTextEl){
49404             this.collapsedTitleTextEl.innerHTML = title;
49405         }
49406     },
49407
49408     getBox : function(){
49409         var b;
49410         if(!this.collapsed){
49411             b = this.el.getBox(false, true);
49412         }else{
49413             b = this.collapsedEl.getBox(false, true);
49414         }
49415         return b;
49416     },
49417
49418     getMargins : function(){
49419         return this.collapsed ? this.cmargins : this.margins;
49420     },
49421
49422     highlight : function(){
49423         this.el.addClass("x-layout-panel-dragover");
49424     },
49425
49426     unhighlight : function(){
49427         this.el.removeClass("x-layout-panel-dragover");
49428     },
49429
49430     updateBox : function(box){
49431         this.box = box;
49432         if(!this.collapsed){
49433             this.el.dom.style.left = box.x + "px";
49434             this.el.dom.style.top = box.y + "px";
49435             this.updateBody(box.width, box.height);
49436         }else{
49437             this.collapsedEl.dom.style.left = box.x + "px";
49438             this.collapsedEl.dom.style.top = box.y + "px";
49439             this.collapsedEl.setSize(box.width, box.height);
49440         }
49441         if(this.tabs){
49442             this.tabs.autoSizeTabs();
49443         }
49444     },
49445
49446     updateBody : function(w, h){
49447         if(w !== null){
49448             this.el.setWidth(w);
49449             w -= this.el.getBorderWidth("rl");
49450             if(this.config.adjustments){
49451                 w += this.config.adjustments[0];
49452             }
49453         }
49454         if(h !== null){
49455             this.el.setHeight(h);
49456             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49457             h -= this.el.getBorderWidth("tb");
49458             if(this.config.adjustments){
49459                 h += this.config.adjustments[1];
49460             }
49461             this.bodyEl.setHeight(h);
49462             if(this.tabs){
49463                 h = this.tabs.syncHeight(h);
49464             }
49465         }
49466         if(this.panelSize){
49467             w = w !== null ? w : this.panelSize.width;
49468             h = h !== null ? h : this.panelSize.height;
49469         }
49470         if(this.activePanel){
49471             var el = this.activePanel.getEl();
49472             w = w !== null ? w : el.getWidth();
49473             h = h !== null ? h : el.getHeight();
49474             this.panelSize = {width: w, height: h};
49475             this.activePanel.setSize(w, h);
49476         }
49477         if(Roo.isIE && this.tabs){
49478             this.tabs.el.repaint();
49479         }
49480     },
49481
49482     /**
49483      * Returns the container element for this region.
49484      * @return {Roo.Element}
49485      */
49486     getEl : function(){
49487         return this.el;
49488     },
49489
49490     /**
49491      * Hides this region.
49492      */
49493     hide : function(){
49494         if(!this.collapsed){
49495             this.el.dom.style.left = "-2000px";
49496             this.el.hide();
49497         }else{
49498             this.collapsedEl.dom.style.left = "-2000px";
49499             this.collapsedEl.hide();
49500         }
49501         this.visible = false;
49502         this.fireEvent("visibilitychange", this, false);
49503     },
49504
49505     /**
49506      * Shows this region if it was previously hidden.
49507      */
49508     show : function(){
49509         if(!this.collapsed){
49510             this.el.show();
49511         }else{
49512             this.collapsedEl.show();
49513         }
49514         this.visible = true;
49515         this.fireEvent("visibilitychange", this, true);
49516     },
49517
49518     closeClicked : function(){
49519         if(this.activePanel){
49520             this.remove(this.activePanel);
49521         }
49522     },
49523
49524     collapseClick : function(e){
49525         if(this.isSlid){
49526            e.stopPropagation();
49527            this.slideIn();
49528         }else{
49529            e.stopPropagation();
49530            this.slideOut();
49531         }
49532     },
49533
49534     /**
49535      * Collapses this region.
49536      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49537      */
49538     collapse : function(skipAnim){
49539         if(this.collapsed) return;
49540         this.collapsed = true;
49541         if(this.split){
49542             this.split.el.hide();
49543         }
49544         if(this.config.animate && skipAnim !== true){
49545             this.fireEvent("invalidated", this);
49546             this.animateCollapse();
49547         }else{
49548             this.el.setLocation(-20000,-20000);
49549             this.el.hide();
49550             this.collapsedEl.show();
49551             this.fireEvent("collapsed", this);
49552             this.fireEvent("invalidated", this);
49553         }
49554     },
49555
49556     animateCollapse : function(){
49557         // overridden
49558     },
49559
49560     /**
49561      * Expands this region if it was previously collapsed.
49562      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49563      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49564      */
49565     expand : function(e, skipAnim){
49566         if(e) e.stopPropagation();
49567         if(!this.collapsed || this.el.hasActiveFx()) return;
49568         if(this.isSlid){
49569             this.afterSlideIn();
49570             skipAnim = true;
49571         }
49572         this.collapsed = false;
49573         if(this.config.animate && skipAnim !== true){
49574             this.animateExpand();
49575         }else{
49576             this.el.show();
49577             if(this.split){
49578                 this.split.el.show();
49579             }
49580             this.collapsedEl.setLocation(-2000,-2000);
49581             this.collapsedEl.hide();
49582             this.fireEvent("invalidated", this);
49583             this.fireEvent("expanded", this);
49584         }
49585     },
49586
49587     animateExpand : function(){
49588         // overridden
49589     },
49590
49591     initTabs : function()
49592     {
49593         this.bodyEl.setStyle("overflow", "hidden");
49594         var ts = new Roo.TabPanel(
49595                 this.bodyEl.dom,
49596                 {
49597                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49598                     disableTooltips: this.config.disableTabTips,
49599                     toolbar : this.config.toolbar
49600                 }
49601         );
49602         if(this.config.hideTabs){
49603             ts.stripWrap.setDisplayed(false);
49604         }
49605         this.tabs = ts;
49606         ts.resizeTabs = this.config.resizeTabs === true;
49607         ts.minTabWidth = this.config.minTabWidth || 40;
49608         ts.maxTabWidth = this.config.maxTabWidth || 250;
49609         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49610         ts.monitorResize = false;
49611         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49612         ts.bodyEl.addClass('x-layout-tabs-body');
49613         this.panels.each(this.initPanelAsTab, this);
49614     },
49615
49616     initPanelAsTab : function(panel){
49617         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49618                     this.config.closeOnTab && panel.isClosable());
49619         if(panel.tabTip !== undefined){
49620             ti.setTooltip(panel.tabTip);
49621         }
49622         ti.on("activate", function(){
49623               this.setActivePanel(panel);
49624         }, this);
49625         if(this.config.closeOnTab){
49626             ti.on("beforeclose", function(t, e){
49627                 e.cancel = true;
49628                 this.remove(panel);
49629             }, this);
49630         }
49631         return ti;
49632     },
49633
49634     updatePanelTitle : function(panel, title){
49635         if(this.activePanel == panel){
49636             this.updateTitle(title);
49637         }
49638         if(this.tabs){
49639             var ti = this.tabs.getTab(panel.getEl().id);
49640             ti.setText(title);
49641             if(panel.tabTip !== undefined){
49642                 ti.setTooltip(panel.tabTip);
49643             }
49644         }
49645     },
49646
49647     updateTitle : function(title){
49648         if(this.titleTextEl && !this.config.title){
49649             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49650         }
49651     },
49652
49653     setActivePanel : function(panel){
49654         panel = this.getPanel(panel);
49655         if(this.activePanel && this.activePanel != panel){
49656             this.activePanel.setActiveState(false);
49657         }
49658         this.activePanel = panel;
49659         panel.setActiveState(true);
49660         if(this.panelSize){
49661             panel.setSize(this.panelSize.width, this.panelSize.height);
49662         }
49663         if(this.closeBtn){
49664             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49665         }
49666         this.updateTitle(panel.getTitle());
49667         if(this.tabs){
49668             this.fireEvent("invalidated", this);
49669         }
49670         this.fireEvent("panelactivated", this, panel);
49671     },
49672
49673     /**
49674      * Shows the specified panel.
49675      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49676      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49677      */
49678     showPanel : function(panel){
49679         if(panel = this.getPanel(panel)){
49680             if(this.tabs){
49681                 var tab = this.tabs.getTab(panel.getEl().id);
49682                 if(tab.isHidden()){
49683                     this.tabs.unhideTab(tab.id);
49684                 }
49685                 tab.activate();
49686             }else{
49687                 this.setActivePanel(panel);
49688             }
49689         }
49690         return panel;
49691     },
49692
49693     /**
49694      * Get the active panel for this region.
49695      * @return {Roo.ContentPanel} The active panel or null
49696      */
49697     getActivePanel : function(){
49698         return this.activePanel;
49699     },
49700
49701     validateVisibility : function(){
49702         if(this.panels.getCount() < 1){
49703             this.updateTitle("&#160;");
49704             this.closeBtn.hide();
49705             this.hide();
49706         }else{
49707             if(!this.isVisible()){
49708                 this.show();
49709             }
49710         }
49711     },
49712
49713     /**
49714      * Adds the passed ContentPanel(s) to this region.
49715      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49716      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49717      */
49718     add : function(panel){
49719         if(arguments.length > 1){
49720             for(var i = 0, len = arguments.length; i < len; i++) {
49721                 this.add(arguments[i]);
49722             }
49723             return null;
49724         }
49725         if(this.hasPanel(panel)){
49726             this.showPanel(panel);
49727             return panel;
49728         }
49729         panel.setRegion(this);
49730         this.panels.add(panel);
49731         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49732             this.bodyEl.dom.appendChild(panel.getEl().dom);
49733             if(panel.background !== true){
49734                 this.setActivePanel(panel);
49735             }
49736             this.fireEvent("paneladded", this, panel);
49737             return panel;
49738         }
49739         if(!this.tabs){
49740             this.initTabs();
49741         }else{
49742             this.initPanelAsTab(panel);
49743         }
49744         if(panel.background !== true){
49745             this.tabs.activate(panel.getEl().id);
49746         }
49747         this.fireEvent("paneladded", this, panel);
49748         return panel;
49749     },
49750
49751     /**
49752      * Hides the tab for the specified panel.
49753      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49754      */
49755     hidePanel : function(panel){
49756         if(this.tabs && (panel = this.getPanel(panel))){
49757             this.tabs.hideTab(panel.getEl().id);
49758         }
49759     },
49760
49761     /**
49762      * Unhides the tab for a previously hidden panel.
49763      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49764      */
49765     unhidePanel : function(panel){
49766         if(this.tabs && (panel = this.getPanel(panel))){
49767             this.tabs.unhideTab(panel.getEl().id);
49768         }
49769     },
49770
49771     clearPanels : function(){
49772         while(this.panels.getCount() > 0){
49773              this.remove(this.panels.first());
49774         }
49775     },
49776
49777     /**
49778      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49779      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49780      * @param {Boolean} preservePanel Overrides the config preservePanel option
49781      * @return {Roo.ContentPanel} The panel that was removed
49782      */
49783     remove : function(panel, preservePanel){
49784         panel = this.getPanel(panel);
49785         if(!panel){
49786             return null;
49787         }
49788         var e = {};
49789         this.fireEvent("beforeremove", this, panel, e);
49790         if(e.cancel === true){
49791             return null;
49792         }
49793         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49794         var panelId = panel.getId();
49795         this.panels.removeKey(panelId);
49796         if(preservePanel){
49797             document.body.appendChild(panel.getEl().dom);
49798         }
49799         if(this.tabs){
49800             this.tabs.removeTab(panel.getEl().id);
49801         }else if (!preservePanel){
49802             this.bodyEl.dom.removeChild(panel.getEl().dom);
49803         }
49804         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49805             var p = this.panels.first();
49806             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49807             tempEl.appendChild(p.getEl().dom);
49808             this.bodyEl.update("");
49809             this.bodyEl.dom.appendChild(p.getEl().dom);
49810             tempEl = null;
49811             this.updateTitle(p.getTitle());
49812             this.tabs = null;
49813             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49814             this.setActivePanel(p);
49815         }
49816         panel.setRegion(null);
49817         if(this.activePanel == panel){
49818             this.activePanel = null;
49819         }
49820         if(this.config.autoDestroy !== false && preservePanel !== true){
49821             try{panel.destroy();}catch(e){}
49822         }
49823         this.fireEvent("panelremoved", this, panel);
49824         return panel;
49825     },
49826
49827     /**
49828      * Returns the TabPanel component used by this region
49829      * @return {Roo.TabPanel}
49830      */
49831     getTabs : function(){
49832         return this.tabs;
49833     },
49834
49835     createTool : function(parentEl, className){
49836         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49837             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49838         btn.addClassOnOver("x-layout-tools-button-over");
49839         return btn;
49840     }
49841 });/*
49842  * Based on:
49843  * Ext JS Library 1.1.1
49844  * Copyright(c) 2006-2007, Ext JS, LLC.
49845  *
49846  * Originally Released Under LGPL - original licence link has changed is not relivant.
49847  *
49848  * Fork - LGPL
49849  * <script type="text/javascript">
49850  */
49851  
49852
49853
49854 /**
49855  * @class Roo.SplitLayoutRegion
49856  * @extends Roo.LayoutRegion
49857  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49858  */
49859 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49860     this.cursor = cursor;
49861     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49862 };
49863
49864 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49865     splitTip : "Drag to resize.",
49866     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49867     useSplitTips : false,
49868
49869     applyConfig : function(config){
49870         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49871         if(config.split){
49872             if(!this.split){
49873                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49874                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49875                 /** The SplitBar for this region 
49876                 * @type Roo.SplitBar */
49877                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49878                 this.split.on("moved", this.onSplitMove, this);
49879                 this.split.useShim = config.useShim === true;
49880                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49881                 if(this.useSplitTips){
49882                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49883                 }
49884                 if(config.collapsible){
49885                     this.split.el.on("dblclick", this.collapse,  this);
49886                 }
49887             }
49888             if(typeof config.minSize != "undefined"){
49889                 this.split.minSize = config.minSize;
49890             }
49891             if(typeof config.maxSize != "undefined"){
49892                 this.split.maxSize = config.maxSize;
49893             }
49894             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49895                 this.hideSplitter();
49896             }
49897         }
49898     },
49899
49900     getHMaxSize : function(){
49901          var cmax = this.config.maxSize || 10000;
49902          var center = this.mgr.getRegion("center");
49903          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49904     },
49905
49906     getVMaxSize : function(){
49907          var cmax = this.config.maxSize || 10000;
49908          var center = this.mgr.getRegion("center");
49909          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49910     },
49911
49912     onSplitMove : function(split, newSize){
49913         this.fireEvent("resized", this, newSize);
49914     },
49915     
49916     /** 
49917      * Returns the {@link Roo.SplitBar} for this region.
49918      * @return {Roo.SplitBar}
49919      */
49920     getSplitBar : function(){
49921         return this.split;
49922     },
49923     
49924     hide : function(){
49925         this.hideSplitter();
49926         Roo.SplitLayoutRegion.superclass.hide.call(this);
49927     },
49928
49929     hideSplitter : function(){
49930         if(this.split){
49931             this.split.el.setLocation(-2000,-2000);
49932             this.split.el.hide();
49933         }
49934     },
49935
49936     show : function(){
49937         if(this.split){
49938             this.split.el.show();
49939         }
49940         Roo.SplitLayoutRegion.superclass.show.call(this);
49941     },
49942     
49943     beforeSlide: function(){
49944         if(Roo.isGecko){// firefox overflow auto bug workaround
49945             this.bodyEl.clip();
49946             if(this.tabs) this.tabs.bodyEl.clip();
49947             if(this.activePanel){
49948                 this.activePanel.getEl().clip();
49949                 
49950                 if(this.activePanel.beforeSlide){
49951                     this.activePanel.beforeSlide();
49952                 }
49953             }
49954         }
49955     },
49956     
49957     afterSlide : function(){
49958         if(Roo.isGecko){// firefox overflow auto bug workaround
49959             this.bodyEl.unclip();
49960             if(this.tabs) this.tabs.bodyEl.unclip();
49961             if(this.activePanel){
49962                 this.activePanel.getEl().unclip();
49963                 if(this.activePanel.afterSlide){
49964                     this.activePanel.afterSlide();
49965                 }
49966             }
49967         }
49968     },
49969
49970     initAutoHide : function(){
49971         if(this.autoHide !== false){
49972             if(!this.autoHideHd){
49973                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49974                 this.autoHideHd = {
49975                     "mouseout": function(e){
49976                         if(!e.within(this.el, true)){
49977                             st.delay(500);
49978                         }
49979                     },
49980                     "mouseover" : function(e){
49981                         st.cancel();
49982                     },
49983                     scope : this
49984                 };
49985             }
49986             this.el.on(this.autoHideHd);
49987         }
49988     },
49989
49990     clearAutoHide : function(){
49991         if(this.autoHide !== false){
49992             this.el.un("mouseout", this.autoHideHd.mouseout);
49993             this.el.un("mouseover", this.autoHideHd.mouseover);
49994         }
49995     },
49996
49997     clearMonitor : function(){
49998         Roo.get(document).un("click", this.slideInIf, this);
49999     },
50000
50001     // these names are backwards but not changed for compat
50002     slideOut : function(){
50003         if(this.isSlid || this.el.hasActiveFx()){
50004             return;
50005         }
50006         this.isSlid = true;
50007         if(this.collapseBtn){
50008             this.collapseBtn.hide();
50009         }
50010         this.closeBtnState = this.closeBtn.getStyle('display');
50011         this.closeBtn.hide();
50012         if(this.stickBtn){
50013             this.stickBtn.show();
50014         }
50015         this.el.show();
50016         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50017         this.beforeSlide();
50018         this.el.setStyle("z-index", 10001);
50019         this.el.slideIn(this.getSlideAnchor(), {
50020             callback: function(){
50021                 this.afterSlide();
50022                 this.initAutoHide();
50023                 Roo.get(document).on("click", this.slideInIf, this);
50024                 this.fireEvent("slideshow", this);
50025             },
50026             scope: this,
50027             block: true
50028         });
50029     },
50030
50031     afterSlideIn : function(){
50032         this.clearAutoHide();
50033         this.isSlid = false;
50034         this.clearMonitor();
50035         this.el.setStyle("z-index", "");
50036         if(this.collapseBtn){
50037             this.collapseBtn.show();
50038         }
50039         this.closeBtn.setStyle('display', this.closeBtnState);
50040         if(this.stickBtn){
50041             this.stickBtn.hide();
50042         }
50043         this.fireEvent("slidehide", this);
50044     },
50045
50046     slideIn : function(cb){
50047         if(!this.isSlid || this.el.hasActiveFx()){
50048             Roo.callback(cb);
50049             return;
50050         }
50051         this.isSlid = false;
50052         this.beforeSlide();
50053         this.el.slideOut(this.getSlideAnchor(), {
50054             callback: function(){
50055                 this.el.setLeftTop(-10000, -10000);
50056                 this.afterSlide();
50057                 this.afterSlideIn();
50058                 Roo.callback(cb);
50059             },
50060             scope: this,
50061             block: true
50062         });
50063     },
50064     
50065     slideInIf : function(e){
50066         if(!e.within(this.el)){
50067             this.slideIn();
50068         }
50069     },
50070
50071     animateCollapse : function(){
50072         this.beforeSlide();
50073         this.el.setStyle("z-index", 20000);
50074         var anchor = this.getSlideAnchor();
50075         this.el.slideOut(anchor, {
50076             callback : function(){
50077                 this.el.setStyle("z-index", "");
50078                 this.collapsedEl.slideIn(anchor, {duration:.3});
50079                 this.afterSlide();
50080                 this.el.setLocation(-10000,-10000);
50081                 this.el.hide();
50082                 this.fireEvent("collapsed", this);
50083             },
50084             scope: this,
50085             block: true
50086         });
50087     },
50088
50089     animateExpand : function(){
50090         this.beforeSlide();
50091         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50092         this.el.setStyle("z-index", 20000);
50093         this.collapsedEl.hide({
50094             duration:.1
50095         });
50096         this.el.slideIn(this.getSlideAnchor(), {
50097             callback : function(){
50098                 this.el.setStyle("z-index", "");
50099                 this.afterSlide();
50100                 if(this.split){
50101                     this.split.el.show();
50102                 }
50103                 this.fireEvent("invalidated", this);
50104                 this.fireEvent("expanded", this);
50105             },
50106             scope: this,
50107             block: true
50108         });
50109     },
50110
50111     anchors : {
50112         "west" : "left",
50113         "east" : "right",
50114         "north" : "top",
50115         "south" : "bottom"
50116     },
50117
50118     sanchors : {
50119         "west" : "l",
50120         "east" : "r",
50121         "north" : "t",
50122         "south" : "b"
50123     },
50124
50125     canchors : {
50126         "west" : "tl-tr",
50127         "east" : "tr-tl",
50128         "north" : "tl-bl",
50129         "south" : "bl-tl"
50130     },
50131
50132     getAnchor : function(){
50133         return this.anchors[this.position];
50134     },
50135
50136     getCollapseAnchor : function(){
50137         return this.canchors[this.position];
50138     },
50139
50140     getSlideAnchor : function(){
50141         return this.sanchors[this.position];
50142     },
50143
50144     getAlignAdj : function(){
50145         var cm = this.cmargins;
50146         switch(this.position){
50147             case "west":
50148                 return [0, 0];
50149             break;
50150             case "east":
50151                 return [0, 0];
50152             break;
50153             case "north":
50154                 return [0, 0];
50155             break;
50156             case "south":
50157                 return [0, 0];
50158             break;
50159         }
50160     },
50161
50162     getExpandAdj : function(){
50163         var c = this.collapsedEl, cm = this.cmargins;
50164         switch(this.position){
50165             case "west":
50166                 return [-(cm.right+c.getWidth()+cm.left), 0];
50167             break;
50168             case "east":
50169                 return [cm.right+c.getWidth()+cm.left, 0];
50170             break;
50171             case "north":
50172                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50173             break;
50174             case "south":
50175                 return [0, cm.top+cm.bottom+c.getHeight()];
50176             break;
50177         }
50178     }
50179 });/*
50180  * Based on:
50181  * Ext JS Library 1.1.1
50182  * Copyright(c) 2006-2007, Ext JS, LLC.
50183  *
50184  * Originally Released Under LGPL - original licence link has changed is not relivant.
50185  *
50186  * Fork - LGPL
50187  * <script type="text/javascript">
50188  */
50189 /*
50190  * These classes are private internal classes
50191  */
50192 Roo.CenterLayoutRegion = function(mgr, config){
50193     Roo.LayoutRegion.call(this, mgr, config, "center");
50194     this.visible = true;
50195     this.minWidth = config.minWidth || 20;
50196     this.minHeight = config.minHeight || 20;
50197 };
50198
50199 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50200     hide : function(){
50201         // center panel can't be hidden
50202     },
50203     
50204     show : function(){
50205         // center panel can't be hidden
50206     },
50207     
50208     getMinWidth: function(){
50209         return this.minWidth;
50210     },
50211     
50212     getMinHeight: function(){
50213         return this.minHeight;
50214     }
50215 });
50216
50217
50218 Roo.NorthLayoutRegion = function(mgr, config){
50219     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50220     if(this.split){
50221         this.split.placement = Roo.SplitBar.TOP;
50222         this.split.orientation = Roo.SplitBar.VERTICAL;
50223         this.split.el.addClass("x-layout-split-v");
50224     }
50225     var size = config.initialSize || config.height;
50226     if(typeof size != "undefined"){
50227         this.el.setHeight(size);
50228     }
50229 };
50230 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50231     orientation: Roo.SplitBar.VERTICAL,
50232     getBox : function(){
50233         if(this.collapsed){
50234             return this.collapsedEl.getBox();
50235         }
50236         var box = this.el.getBox();
50237         if(this.split){
50238             box.height += this.split.el.getHeight();
50239         }
50240         return box;
50241     },
50242     
50243     updateBox : function(box){
50244         if(this.split && !this.collapsed){
50245             box.height -= this.split.el.getHeight();
50246             this.split.el.setLeft(box.x);
50247             this.split.el.setTop(box.y+box.height);
50248             this.split.el.setWidth(box.width);
50249         }
50250         if(this.collapsed){
50251             this.updateBody(box.width, null);
50252         }
50253         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50254     }
50255 });
50256
50257 Roo.SouthLayoutRegion = function(mgr, config){
50258     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50259     if(this.split){
50260         this.split.placement = Roo.SplitBar.BOTTOM;
50261         this.split.orientation = Roo.SplitBar.VERTICAL;
50262         this.split.el.addClass("x-layout-split-v");
50263     }
50264     var size = config.initialSize || config.height;
50265     if(typeof size != "undefined"){
50266         this.el.setHeight(size);
50267     }
50268 };
50269 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50270     orientation: Roo.SplitBar.VERTICAL,
50271     getBox : function(){
50272         if(this.collapsed){
50273             return this.collapsedEl.getBox();
50274         }
50275         var box = this.el.getBox();
50276         if(this.split){
50277             var sh = this.split.el.getHeight();
50278             box.height += sh;
50279             box.y -= sh;
50280         }
50281         return box;
50282     },
50283     
50284     updateBox : function(box){
50285         if(this.split && !this.collapsed){
50286             var sh = this.split.el.getHeight();
50287             box.height -= sh;
50288             box.y += sh;
50289             this.split.el.setLeft(box.x);
50290             this.split.el.setTop(box.y-sh);
50291             this.split.el.setWidth(box.width);
50292         }
50293         if(this.collapsed){
50294             this.updateBody(box.width, null);
50295         }
50296         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50297     }
50298 });
50299
50300 Roo.EastLayoutRegion = function(mgr, config){
50301     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50302     if(this.split){
50303         this.split.placement = Roo.SplitBar.RIGHT;
50304         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50305         this.split.el.addClass("x-layout-split-h");
50306     }
50307     var size = config.initialSize || config.width;
50308     if(typeof size != "undefined"){
50309         this.el.setWidth(size);
50310     }
50311 };
50312 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50313     orientation: Roo.SplitBar.HORIZONTAL,
50314     getBox : function(){
50315         if(this.collapsed){
50316             return this.collapsedEl.getBox();
50317         }
50318         var box = this.el.getBox();
50319         if(this.split){
50320             var sw = this.split.el.getWidth();
50321             box.width += sw;
50322             box.x -= sw;
50323         }
50324         return box;
50325     },
50326
50327     updateBox : function(box){
50328         if(this.split && !this.collapsed){
50329             var sw = this.split.el.getWidth();
50330             box.width -= sw;
50331             this.split.el.setLeft(box.x);
50332             this.split.el.setTop(box.y);
50333             this.split.el.setHeight(box.height);
50334             box.x += sw;
50335         }
50336         if(this.collapsed){
50337             this.updateBody(null, box.height);
50338         }
50339         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50340     }
50341 });
50342
50343 Roo.WestLayoutRegion = function(mgr, config){
50344     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50345     if(this.split){
50346         this.split.placement = Roo.SplitBar.LEFT;
50347         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50348         this.split.el.addClass("x-layout-split-h");
50349     }
50350     var size = config.initialSize || config.width;
50351     if(typeof size != "undefined"){
50352         this.el.setWidth(size);
50353     }
50354 };
50355 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50356     orientation: Roo.SplitBar.HORIZONTAL,
50357     getBox : function(){
50358         if(this.collapsed){
50359             return this.collapsedEl.getBox();
50360         }
50361         var box = this.el.getBox();
50362         if(this.split){
50363             box.width += this.split.el.getWidth();
50364         }
50365         return box;
50366     },
50367     
50368     updateBox : function(box){
50369         if(this.split && !this.collapsed){
50370             var sw = this.split.el.getWidth();
50371             box.width -= sw;
50372             this.split.el.setLeft(box.x+box.width);
50373             this.split.el.setTop(box.y);
50374             this.split.el.setHeight(box.height);
50375         }
50376         if(this.collapsed){
50377             this.updateBody(null, box.height);
50378         }
50379         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50380     }
50381 });
50382 /*
50383  * Based on:
50384  * Ext JS Library 1.1.1
50385  * Copyright(c) 2006-2007, Ext JS, LLC.
50386  *
50387  * Originally Released Under LGPL - original licence link has changed is not relivant.
50388  *
50389  * Fork - LGPL
50390  * <script type="text/javascript">
50391  */
50392  
50393  
50394 /*
50395  * Private internal class for reading and applying state
50396  */
50397 Roo.LayoutStateManager = function(layout){
50398      // default empty state
50399      this.state = {
50400         north: {},
50401         south: {},
50402         east: {},
50403         west: {}       
50404     };
50405 };
50406
50407 Roo.LayoutStateManager.prototype = {
50408     init : function(layout, provider){
50409         this.provider = provider;
50410         var state = provider.get(layout.id+"-layout-state");
50411         if(state){
50412             var wasUpdating = layout.isUpdating();
50413             if(!wasUpdating){
50414                 layout.beginUpdate();
50415             }
50416             for(var key in state){
50417                 if(typeof state[key] != "function"){
50418                     var rstate = state[key];
50419                     var r = layout.getRegion(key);
50420                     if(r && rstate){
50421                         if(rstate.size){
50422                             r.resizeTo(rstate.size);
50423                         }
50424                         if(rstate.collapsed == true){
50425                             r.collapse(true);
50426                         }else{
50427                             r.expand(null, true);
50428                         }
50429                     }
50430                 }
50431             }
50432             if(!wasUpdating){
50433                 layout.endUpdate();
50434             }
50435             this.state = state; 
50436         }
50437         this.layout = layout;
50438         layout.on("regionresized", this.onRegionResized, this);
50439         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50440         layout.on("regionexpanded", this.onRegionExpanded, this);
50441     },
50442     
50443     storeState : function(){
50444         this.provider.set(this.layout.id+"-layout-state", this.state);
50445     },
50446     
50447     onRegionResized : function(region, newSize){
50448         this.state[region.getPosition()].size = newSize;
50449         this.storeState();
50450     },
50451     
50452     onRegionCollapsed : function(region){
50453         this.state[region.getPosition()].collapsed = true;
50454         this.storeState();
50455     },
50456     
50457     onRegionExpanded : function(region){
50458         this.state[region.getPosition()].collapsed = false;
50459         this.storeState();
50460     }
50461 };/*
50462  * Based on:
50463  * Ext JS Library 1.1.1
50464  * Copyright(c) 2006-2007, Ext JS, LLC.
50465  *
50466  * Originally Released Under LGPL - original licence link has changed is not relivant.
50467  *
50468  * Fork - LGPL
50469  * <script type="text/javascript">
50470  */
50471 /**
50472  * @class Roo.ContentPanel
50473  * @extends Roo.util.Observable
50474  * A basic ContentPanel element.
50475  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50476  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50477  * @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
50478  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50479  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50480  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50481  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50482  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50483  * @cfg {String} title          The title for this panel
50484  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50485  * @cfg {String} url            Calls {@link #setUrl} with this value
50486  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50487  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50488  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50489  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50490
50491  * @constructor
50492  * Create a new ContentPanel.
50493  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50494  * @param {String/Object} config A string to set only the title or a config object
50495  * @param {String} content (optional) Set the HTML content for this panel
50496  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50497  */
50498 Roo.ContentPanel = function(el, config, content){
50499     
50500      
50501     /*
50502     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50503         config = el;
50504         el = Roo.id();
50505     }
50506     if (config && config.parentLayout) { 
50507         el = config.parentLayout.el.createChild(); 
50508     }
50509     */
50510     if(el.autoCreate){ // xtype is available if this is called from factory
50511         config = el;
50512         el = Roo.id();
50513     }
50514     this.el = Roo.get(el);
50515     if(!this.el && config && config.autoCreate){
50516         if(typeof config.autoCreate == "object"){
50517             if(!config.autoCreate.id){
50518                 config.autoCreate.id = config.id||el;
50519             }
50520             this.el = Roo.DomHelper.append(document.body,
50521                         config.autoCreate, true);
50522         }else{
50523             this.el = Roo.DomHelper.append(document.body,
50524                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50525         }
50526     }
50527     this.closable = false;
50528     this.loaded = false;
50529     this.active = false;
50530     if(typeof config == "string"){
50531         this.title = config;
50532     }else{
50533         Roo.apply(this, config);
50534     }
50535     
50536     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50537         this.wrapEl = this.el.wrap();
50538         this.toolbar.container = this.el.insertSibling(false, 'before');
50539         this.toolbar = new Roo.Toolbar(this.toolbar);
50540     }
50541     
50542     // xtype created footer. - not sure if will work as we normally have to render first..
50543     if (this.footer && !this.footer.el && this.footer.xtype) {
50544         if (!this.wrapEl) {
50545             this.wrapEl = this.el.wrap();
50546         }
50547     
50548         this.footer.container = this.wrapEl.createChild();
50549          
50550         this.footer = Roo.factory(this.footer, Roo);
50551         
50552     }
50553     
50554     if(this.resizeEl){
50555         this.resizeEl = Roo.get(this.resizeEl, true);
50556     }else{
50557         this.resizeEl = this.el;
50558     }
50559     // handle view.xtype
50560     
50561  
50562     
50563     
50564     this.addEvents({
50565         /**
50566          * @event activate
50567          * Fires when this panel is activated. 
50568          * @param {Roo.ContentPanel} this
50569          */
50570         "activate" : true,
50571         /**
50572          * @event deactivate
50573          * Fires when this panel is activated. 
50574          * @param {Roo.ContentPanel} this
50575          */
50576         "deactivate" : true,
50577
50578         /**
50579          * @event resize
50580          * Fires when this panel is resized if fitToFrame is true.
50581          * @param {Roo.ContentPanel} this
50582          * @param {Number} width The width after any component adjustments
50583          * @param {Number} height The height after any component adjustments
50584          */
50585         "resize" : true,
50586         
50587          /**
50588          * @event render
50589          * Fires when this tab is created
50590          * @param {Roo.ContentPanel} this
50591          */
50592         "render" : true
50593         
50594         
50595         
50596     });
50597     
50598
50599     
50600     
50601     if(this.autoScroll){
50602         this.resizeEl.setStyle("overflow", "auto");
50603     } else {
50604         // fix randome scrolling
50605         this.el.on('scroll', function() {
50606             Roo.log('fix random scolling');
50607             this.scrollTo('top',0); 
50608         });
50609     }
50610     content = content || this.content;
50611     if(content){
50612         this.setContent(content);
50613     }
50614     if(config && config.url){
50615         this.setUrl(this.url, this.params, this.loadOnce);
50616     }
50617     
50618     
50619     
50620     Roo.ContentPanel.superclass.constructor.call(this);
50621     
50622     if (this.view && typeof(this.view.xtype) != 'undefined') {
50623         this.view.el = this.el.appendChild(document.createElement("div"));
50624         this.view = Roo.factory(this.view); 
50625         this.view.render  &&  this.view.render(false, '');  
50626     }
50627     
50628     
50629     this.fireEvent('render', this);
50630 };
50631
50632 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50633     tabTip:'',
50634     setRegion : function(region){
50635         this.region = region;
50636         if(region){
50637            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50638         }else{
50639            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50640         } 
50641     },
50642     
50643     /**
50644      * Returns the toolbar for this Panel if one was configured. 
50645      * @return {Roo.Toolbar} 
50646      */
50647     getToolbar : function(){
50648         return this.toolbar;
50649     },
50650     
50651     setActiveState : function(active){
50652         this.active = active;
50653         if(!active){
50654             this.fireEvent("deactivate", this);
50655         }else{
50656             this.fireEvent("activate", this);
50657         }
50658     },
50659     /**
50660      * Updates this panel's element
50661      * @param {String} content The new content
50662      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50663     */
50664     setContent : function(content, loadScripts){
50665         this.el.update(content, loadScripts);
50666     },
50667
50668     ignoreResize : function(w, h){
50669         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50670             return true;
50671         }else{
50672             this.lastSize = {width: w, height: h};
50673             return false;
50674         }
50675     },
50676     /**
50677      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50678      * @return {Roo.UpdateManager} The UpdateManager
50679      */
50680     getUpdateManager : function(){
50681         return this.el.getUpdateManager();
50682     },
50683      /**
50684      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50685      * @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:
50686 <pre><code>
50687 panel.load({
50688     url: "your-url.php",
50689     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50690     callback: yourFunction,
50691     scope: yourObject, //(optional scope)
50692     discardUrl: false,
50693     nocache: false,
50694     text: "Loading...",
50695     timeout: 30,
50696     scripts: false
50697 });
50698 </code></pre>
50699      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50700      * 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.
50701      * @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}
50702      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50703      * @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.
50704      * @return {Roo.ContentPanel} this
50705      */
50706     load : function(){
50707         var um = this.el.getUpdateManager();
50708         um.update.apply(um, arguments);
50709         return this;
50710     },
50711
50712
50713     /**
50714      * 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.
50715      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50716      * @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)
50717      * @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)
50718      * @return {Roo.UpdateManager} The UpdateManager
50719      */
50720     setUrl : function(url, params, loadOnce){
50721         if(this.refreshDelegate){
50722             this.removeListener("activate", this.refreshDelegate);
50723         }
50724         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50725         this.on("activate", this.refreshDelegate);
50726         return this.el.getUpdateManager();
50727     },
50728     
50729     _handleRefresh : function(url, params, loadOnce){
50730         if(!loadOnce || !this.loaded){
50731             var updater = this.el.getUpdateManager();
50732             updater.update(url, params, this._setLoaded.createDelegate(this));
50733         }
50734     },
50735     
50736     _setLoaded : function(){
50737         this.loaded = true;
50738     }, 
50739     
50740     /**
50741      * Returns this panel's id
50742      * @return {String} 
50743      */
50744     getId : function(){
50745         return this.el.id;
50746     },
50747     
50748     /** 
50749      * Returns this panel's element - used by regiosn to add.
50750      * @return {Roo.Element} 
50751      */
50752     getEl : function(){
50753         return this.wrapEl || this.el;
50754     },
50755     
50756     adjustForComponents : function(width, height)
50757     {
50758         //Roo.log('adjustForComponents ');
50759         if(this.resizeEl != this.el){
50760             width -= this.el.getFrameWidth('lr');
50761             height -= this.el.getFrameWidth('tb');
50762         }
50763         if(this.toolbar){
50764             var te = this.toolbar.getEl();
50765             height -= te.getHeight();
50766             te.setWidth(width);
50767         }
50768         if(this.footer){
50769             var te = this.footer.getEl();
50770             Roo.log("footer:" + te.getHeight());
50771             
50772             height -= te.getHeight();
50773             te.setWidth(width);
50774         }
50775         
50776         
50777         if(this.adjustments){
50778             width += this.adjustments[0];
50779             height += this.adjustments[1];
50780         }
50781         return {"width": width, "height": height};
50782     },
50783     
50784     setSize : function(width, height){
50785         if(this.fitToFrame && !this.ignoreResize(width, height)){
50786             if(this.fitContainer && this.resizeEl != this.el){
50787                 this.el.setSize(width, height);
50788             }
50789             var size = this.adjustForComponents(width, height);
50790             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50791             this.fireEvent('resize', this, size.width, size.height);
50792         }
50793     },
50794     
50795     /**
50796      * Returns this panel's title
50797      * @return {String} 
50798      */
50799     getTitle : function(){
50800         return this.title;
50801     },
50802     
50803     /**
50804      * Set this panel's title
50805      * @param {String} title
50806      */
50807     setTitle : function(title){
50808         this.title = title;
50809         if(this.region){
50810             this.region.updatePanelTitle(this, title);
50811         }
50812     },
50813     
50814     /**
50815      * Returns true is this panel was configured to be closable
50816      * @return {Boolean} 
50817      */
50818     isClosable : function(){
50819         return this.closable;
50820     },
50821     
50822     beforeSlide : function(){
50823         this.el.clip();
50824         this.resizeEl.clip();
50825     },
50826     
50827     afterSlide : function(){
50828         this.el.unclip();
50829         this.resizeEl.unclip();
50830     },
50831     
50832     /**
50833      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50834      *   Will fail silently if the {@link #setUrl} method has not been called.
50835      *   This does not activate the panel, just updates its content.
50836      */
50837     refresh : function(){
50838         if(this.refreshDelegate){
50839            this.loaded = false;
50840            this.refreshDelegate();
50841         }
50842     },
50843     
50844     /**
50845      * Destroys this panel
50846      */
50847     destroy : function(){
50848         this.el.removeAllListeners();
50849         var tempEl = document.createElement("span");
50850         tempEl.appendChild(this.el.dom);
50851         tempEl.innerHTML = "";
50852         this.el.remove();
50853         this.el = null;
50854     },
50855     
50856     /**
50857      * form - if the content panel contains a form - this is a reference to it.
50858      * @type {Roo.form.Form}
50859      */
50860     form : false,
50861     /**
50862      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50863      *    This contains a reference to it.
50864      * @type {Roo.View}
50865      */
50866     view : false,
50867     
50868       /**
50869      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50870      * <pre><code>
50871
50872 layout.addxtype({
50873        xtype : 'Form',
50874        items: [ .... ]
50875    }
50876 );
50877
50878 </code></pre>
50879      * @param {Object} cfg Xtype definition of item to add.
50880      */
50881     
50882     addxtype : function(cfg) {
50883         // add form..
50884         if (cfg.xtype.match(/^Form$/)) {
50885             
50886             var el;
50887             //if (this.footer) {
50888             //    el = this.footer.container.insertSibling(false, 'before');
50889             //} else {
50890                 el = this.el.createChild();
50891             //}
50892
50893             this.form = new  Roo.form.Form(cfg);
50894             
50895             
50896             if ( this.form.allItems.length) this.form.render(el.dom);
50897             return this.form;
50898         }
50899         // should only have one of theses..
50900         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50901             // views.. should not be just added - used named prop 'view''
50902             
50903             cfg.el = this.el.appendChild(document.createElement("div"));
50904             // factory?
50905             
50906             var ret = new Roo.factory(cfg);
50907              
50908              ret.render && ret.render(false, ''); // render blank..
50909             this.view = ret;
50910             return ret;
50911         }
50912         return false;
50913     }
50914 });
50915
50916 /**
50917  * @class Roo.GridPanel
50918  * @extends Roo.ContentPanel
50919  * @constructor
50920  * Create a new GridPanel.
50921  * @param {Roo.grid.Grid} grid The grid for this panel
50922  * @param {String/Object} config A string to set only the panel's title, or a config object
50923  */
50924 Roo.GridPanel = function(grid, config){
50925     
50926   
50927     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50928         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50929         
50930     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50931     
50932     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50933     
50934     if(this.toolbar){
50935         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50936     }
50937     // xtype created footer. - not sure if will work as we normally have to render first..
50938     if (this.footer && !this.footer.el && this.footer.xtype) {
50939         
50940         this.footer.container = this.grid.getView().getFooterPanel(true);
50941         this.footer.dataSource = this.grid.dataSource;
50942         this.footer = Roo.factory(this.footer, Roo);
50943         
50944     }
50945     
50946     grid.monitorWindowResize = false; // turn off autosizing
50947     grid.autoHeight = false;
50948     grid.autoWidth = false;
50949     this.grid = grid;
50950     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50951 };
50952
50953 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50954     getId : function(){
50955         return this.grid.id;
50956     },
50957     
50958     /**
50959      * Returns the grid for this panel
50960      * @return {Roo.grid.Grid} 
50961      */
50962     getGrid : function(){
50963         return this.grid;    
50964     },
50965     
50966     setSize : function(width, height){
50967         if(!this.ignoreResize(width, height)){
50968             var grid = this.grid;
50969             var size = this.adjustForComponents(width, height);
50970             grid.getGridEl().setSize(size.width, size.height);
50971             grid.autoSize();
50972         }
50973     },
50974     
50975     beforeSlide : function(){
50976         this.grid.getView().scroller.clip();
50977     },
50978     
50979     afterSlide : function(){
50980         this.grid.getView().scroller.unclip();
50981     },
50982     
50983     destroy : function(){
50984         this.grid.destroy();
50985         delete this.grid;
50986         Roo.GridPanel.superclass.destroy.call(this); 
50987     }
50988 });
50989
50990
50991 /**
50992  * @class Roo.NestedLayoutPanel
50993  * @extends Roo.ContentPanel
50994  * @constructor
50995  * Create a new NestedLayoutPanel.
50996  * 
50997  * 
50998  * @param {Roo.BorderLayout} layout The layout for this panel
50999  * @param {String/Object} config A string to set only the title or a config object
51000  */
51001 Roo.NestedLayoutPanel = function(layout, config)
51002 {
51003     // construct with only one argument..
51004     /* FIXME - implement nicer consturctors
51005     if (layout.layout) {
51006         config = layout;
51007         layout = config.layout;
51008         delete config.layout;
51009     }
51010     if (layout.xtype && !layout.getEl) {
51011         // then layout needs constructing..
51012         layout = Roo.factory(layout, Roo);
51013     }
51014     */
51015     
51016     
51017     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51018     
51019     layout.monitorWindowResize = false; // turn off autosizing
51020     this.layout = layout;
51021     this.layout.getEl().addClass("x-layout-nested-layout");
51022     
51023     
51024     
51025     
51026 };
51027
51028 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51029
51030     setSize : function(width, height){
51031         if(!this.ignoreResize(width, height)){
51032             var size = this.adjustForComponents(width, height);
51033             var el = this.layout.getEl();
51034             el.setSize(size.width, size.height);
51035             var touch = el.dom.offsetWidth;
51036             this.layout.layout();
51037             // ie requires a double layout on the first pass
51038             if(Roo.isIE && !this.initialized){
51039                 this.initialized = true;
51040                 this.layout.layout();
51041             }
51042         }
51043     },
51044     
51045     // activate all subpanels if not currently active..
51046     
51047     setActiveState : function(active){
51048         this.active = active;
51049         if(!active){
51050             this.fireEvent("deactivate", this);
51051             return;
51052         }
51053         
51054         this.fireEvent("activate", this);
51055         // not sure if this should happen before or after..
51056         if (!this.layout) {
51057             return; // should not happen..
51058         }
51059         var reg = false;
51060         for (var r in this.layout.regions) {
51061             reg = this.layout.getRegion(r);
51062             if (reg.getActivePanel()) {
51063                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51064                 reg.setActivePanel(reg.getActivePanel());
51065                 continue;
51066             }
51067             if (!reg.panels.length) {
51068                 continue;
51069             }
51070             reg.showPanel(reg.getPanel(0));
51071         }
51072         
51073         
51074         
51075         
51076     },
51077     
51078     /**
51079      * Returns the nested BorderLayout for this panel
51080      * @return {Roo.BorderLayout} 
51081      */
51082     getLayout : function(){
51083         return this.layout;
51084     },
51085     
51086      /**
51087      * Adds a xtype elements to the layout of the nested panel
51088      * <pre><code>
51089
51090 panel.addxtype({
51091        xtype : 'ContentPanel',
51092        region: 'west',
51093        items: [ .... ]
51094    }
51095 );
51096
51097 panel.addxtype({
51098         xtype : 'NestedLayoutPanel',
51099         region: 'west',
51100         layout: {
51101            center: { },
51102            west: { }   
51103         },
51104         items : [ ... list of content panels or nested layout panels.. ]
51105    }
51106 );
51107 </code></pre>
51108      * @param {Object} cfg Xtype definition of item to add.
51109      */
51110     addxtype : function(cfg) {
51111         return this.layout.addxtype(cfg);
51112     
51113     }
51114 });
51115
51116 Roo.ScrollPanel = function(el, config, content){
51117     config = config || {};
51118     config.fitToFrame = true;
51119     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51120     
51121     this.el.dom.style.overflow = "hidden";
51122     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51123     this.el.removeClass("x-layout-inactive-content");
51124     this.el.on("mousewheel", this.onWheel, this);
51125
51126     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51127     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51128     up.unselectable(); down.unselectable();
51129     up.on("click", this.scrollUp, this);
51130     down.on("click", this.scrollDown, this);
51131     up.addClassOnOver("x-scroller-btn-over");
51132     down.addClassOnOver("x-scroller-btn-over");
51133     up.addClassOnClick("x-scroller-btn-click");
51134     down.addClassOnClick("x-scroller-btn-click");
51135     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51136
51137     this.resizeEl = this.el;
51138     this.el = wrap; this.up = up; this.down = down;
51139 };
51140
51141 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51142     increment : 100,
51143     wheelIncrement : 5,
51144     scrollUp : function(){
51145         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51146     },
51147
51148     scrollDown : function(){
51149         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51150     },
51151
51152     afterScroll : function(){
51153         var el = this.resizeEl;
51154         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51155         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51156         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51157     },
51158
51159     setSize : function(){
51160         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51161         this.afterScroll();
51162     },
51163
51164     onWheel : function(e){
51165         var d = e.getWheelDelta();
51166         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51167         this.afterScroll();
51168         e.stopEvent();
51169     },
51170
51171     setContent : function(content, loadScripts){
51172         this.resizeEl.update(content, loadScripts);
51173     }
51174
51175 });
51176
51177
51178
51179
51180
51181
51182
51183
51184
51185 /**
51186  * @class Roo.TreePanel
51187  * @extends Roo.ContentPanel
51188  * @constructor
51189  * Create a new TreePanel. - defaults to fit/scoll contents.
51190  * @param {String/Object} config A string to set only the panel's title, or a config object
51191  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51192  */
51193 Roo.TreePanel = function(config){
51194     var el = config.el;
51195     var tree = config.tree;
51196     delete config.tree; 
51197     delete config.el; // hopefull!
51198     
51199     // wrapper for IE7 strict & safari scroll issue
51200     
51201     var treeEl = el.createChild();
51202     config.resizeEl = treeEl;
51203     
51204     
51205     
51206     Roo.TreePanel.superclass.constructor.call(this, el, config);
51207  
51208  
51209     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51210     //console.log(tree);
51211     this.on('activate', function()
51212     {
51213         if (this.tree.rendered) {
51214             return;
51215         }
51216         //console.log('render tree');
51217         this.tree.render();
51218     });
51219     // this should not be needed.. - it's actually the 'el' that resizes?
51220     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51221     
51222     //this.on('resize',  function (cp, w, h) {
51223     //        this.tree.innerCt.setWidth(w);
51224     //        this.tree.innerCt.setHeight(h);
51225     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51226     //});
51227
51228         
51229     
51230 };
51231
51232 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51233     fitToFrame : true,
51234     autoScroll : true
51235 });
51236
51237
51238
51239
51240
51241
51242
51243
51244
51245
51246
51247 /*
51248  * Based on:
51249  * Ext JS Library 1.1.1
51250  * Copyright(c) 2006-2007, Ext JS, LLC.
51251  *
51252  * Originally Released Under LGPL - original licence link has changed is not relivant.
51253  *
51254  * Fork - LGPL
51255  * <script type="text/javascript">
51256  */
51257  
51258
51259 /**
51260  * @class Roo.ReaderLayout
51261  * @extends Roo.BorderLayout
51262  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51263  * center region containing two nested regions (a top one for a list view and one for item preview below),
51264  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51265  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51266  * expedites the setup of the overall layout and regions for this common application style.
51267  * Example:
51268  <pre><code>
51269 var reader = new Roo.ReaderLayout();
51270 var CP = Roo.ContentPanel;  // shortcut for adding
51271
51272 reader.beginUpdate();
51273 reader.add("north", new CP("north", "North"));
51274 reader.add("west", new CP("west", {title: "West"}));
51275 reader.add("east", new CP("east", {title: "East"}));
51276
51277 reader.regions.listView.add(new CP("listView", "List"));
51278 reader.regions.preview.add(new CP("preview", "Preview"));
51279 reader.endUpdate();
51280 </code></pre>
51281 * @constructor
51282 * Create a new ReaderLayout
51283 * @param {Object} config Configuration options
51284 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51285 * document.body if omitted)
51286 */
51287 Roo.ReaderLayout = function(config, renderTo){
51288     var c = config || {size:{}};
51289     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51290         north: c.north !== false ? Roo.apply({
51291             split:false,
51292             initialSize: 32,
51293             titlebar: false
51294         }, c.north) : false,
51295         west: c.west !== false ? Roo.apply({
51296             split:true,
51297             initialSize: 200,
51298             minSize: 175,
51299             maxSize: 400,
51300             titlebar: true,
51301             collapsible: true,
51302             animate: true,
51303             margins:{left:5,right:0,bottom:5,top:5},
51304             cmargins:{left:5,right:5,bottom:5,top:5}
51305         }, c.west) : false,
51306         east: c.east !== false ? Roo.apply({
51307             split:true,
51308             initialSize: 200,
51309             minSize: 175,
51310             maxSize: 400,
51311             titlebar: true,
51312             collapsible: true,
51313             animate: true,
51314             margins:{left:0,right:5,bottom:5,top:5},
51315             cmargins:{left:5,right:5,bottom:5,top:5}
51316         }, c.east) : false,
51317         center: Roo.apply({
51318             tabPosition: 'top',
51319             autoScroll:false,
51320             closeOnTab: true,
51321             titlebar:false,
51322             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51323         }, c.center)
51324     });
51325
51326     this.el.addClass('x-reader');
51327
51328     this.beginUpdate();
51329
51330     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51331         south: c.preview !== false ? Roo.apply({
51332             split:true,
51333             initialSize: 200,
51334             minSize: 100,
51335             autoScroll:true,
51336             collapsible:true,
51337             titlebar: true,
51338             cmargins:{top:5,left:0, right:0, bottom:0}
51339         }, c.preview) : false,
51340         center: Roo.apply({
51341             autoScroll:false,
51342             titlebar:false,
51343             minHeight:200
51344         }, c.listView)
51345     });
51346     this.add('center', new Roo.NestedLayoutPanel(inner,
51347             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51348
51349     this.endUpdate();
51350
51351     this.regions.preview = inner.getRegion('south');
51352     this.regions.listView = inner.getRegion('center');
51353 };
51354
51355 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51356  * Based on:
51357  * Ext JS Library 1.1.1
51358  * Copyright(c) 2006-2007, Ext JS, LLC.
51359  *
51360  * Originally Released Under LGPL - original licence link has changed is not relivant.
51361  *
51362  * Fork - LGPL
51363  * <script type="text/javascript">
51364  */
51365  
51366 /**
51367  * @class Roo.grid.Grid
51368  * @extends Roo.util.Observable
51369  * This class represents the primary interface of a component based grid control.
51370  * <br><br>Usage:<pre><code>
51371  var grid = new Roo.grid.Grid("my-container-id", {
51372      ds: myDataStore,
51373      cm: myColModel,
51374      selModel: mySelectionModel,
51375      autoSizeColumns: true,
51376      monitorWindowResize: false,
51377      trackMouseOver: true
51378  });
51379  // set any options
51380  grid.render();
51381  * </code></pre>
51382  * <b>Common Problems:</b><br/>
51383  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51384  * element will correct this<br/>
51385  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51386  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51387  * are unpredictable.<br/>
51388  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51389  * grid to calculate dimensions/offsets.<br/>
51390   * @constructor
51391  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51392  * The container MUST have some type of size defined for the grid to fill. The container will be
51393  * automatically set to position relative if it isn't already.
51394  * @param {Object} config A config object that sets properties on this grid.
51395  */
51396 Roo.grid.Grid = function(container, config){
51397         // initialize the container
51398         this.container = Roo.get(container);
51399         this.container.update("");
51400         this.container.setStyle("overflow", "hidden");
51401     this.container.addClass('x-grid-container');
51402
51403     this.id = this.container.id;
51404
51405     Roo.apply(this, config);
51406     // check and correct shorthanded configs
51407     if(this.ds){
51408         this.dataSource = this.ds;
51409         delete this.ds;
51410     }
51411     if(this.cm){
51412         this.colModel = this.cm;
51413         delete this.cm;
51414     }
51415     if(this.sm){
51416         this.selModel = this.sm;
51417         delete this.sm;
51418     }
51419
51420     if (this.selModel) {
51421         this.selModel = Roo.factory(this.selModel, Roo.grid);
51422         this.sm = this.selModel;
51423         this.sm.xmodule = this.xmodule || false;
51424     }
51425     if (typeof(this.colModel.config) == 'undefined') {
51426         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51427         this.cm = this.colModel;
51428         this.cm.xmodule = this.xmodule || false;
51429     }
51430     if (this.dataSource) {
51431         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51432         this.ds = this.dataSource;
51433         this.ds.xmodule = this.xmodule || false;
51434          
51435     }
51436     
51437     
51438     
51439     if(this.width){
51440         this.container.setWidth(this.width);
51441     }
51442
51443     if(this.height){
51444         this.container.setHeight(this.height);
51445     }
51446     /** @private */
51447         this.addEvents({
51448         // raw events
51449         /**
51450          * @event click
51451          * The raw click event for the entire grid.
51452          * @param {Roo.EventObject} e
51453          */
51454         "click" : true,
51455         /**
51456          * @event dblclick
51457          * The raw dblclick event for the entire grid.
51458          * @param {Roo.EventObject} e
51459          */
51460         "dblclick" : true,
51461         /**
51462          * @event contextmenu
51463          * The raw contextmenu event for the entire grid.
51464          * @param {Roo.EventObject} e
51465          */
51466         "contextmenu" : true,
51467         /**
51468          * @event mousedown
51469          * The raw mousedown event for the entire grid.
51470          * @param {Roo.EventObject} e
51471          */
51472         "mousedown" : true,
51473         /**
51474          * @event mouseup
51475          * The raw mouseup event for the entire grid.
51476          * @param {Roo.EventObject} e
51477          */
51478         "mouseup" : true,
51479         /**
51480          * @event mouseover
51481          * The raw mouseover event for the entire grid.
51482          * @param {Roo.EventObject} e
51483          */
51484         "mouseover" : true,
51485         /**
51486          * @event mouseout
51487          * The raw mouseout event for the entire grid.
51488          * @param {Roo.EventObject} e
51489          */
51490         "mouseout" : true,
51491         /**
51492          * @event keypress
51493          * The raw keypress event for the entire grid.
51494          * @param {Roo.EventObject} e
51495          */
51496         "keypress" : true,
51497         /**
51498          * @event keydown
51499          * The raw keydown event for the entire grid.
51500          * @param {Roo.EventObject} e
51501          */
51502         "keydown" : true,
51503
51504         // custom events
51505
51506         /**
51507          * @event cellclick
51508          * Fires when a cell is clicked
51509          * @param {Grid} this
51510          * @param {Number} rowIndex
51511          * @param {Number} columnIndex
51512          * @param {Roo.EventObject} e
51513          */
51514         "cellclick" : true,
51515         /**
51516          * @event celldblclick
51517          * Fires when a cell is double clicked
51518          * @param {Grid} this
51519          * @param {Number} rowIndex
51520          * @param {Number} columnIndex
51521          * @param {Roo.EventObject} e
51522          */
51523         "celldblclick" : true,
51524         /**
51525          * @event rowclick
51526          * Fires when a row is clicked
51527          * @param {Grid} this
51528          * @param {Number} rowIndex
51529          * @param {Roo.EventObject} e
51530          */
51531         "rowclick" : true,
51532         /**
51533          * @event rowdblclick
51534          * Fires when a row is double clicked
51535          * @param {Grid} this
51536          * @param {Number} rowIndex
51537          * @param {Roo.EventObject} e
51538          */
51539         "rowdblclick" : true,
51540         /**
51541          * @event headerclick
51542          * Fires when a header is clicked
51543          * @param {Grid} this
51544          * @param {Number} columnIndex
51545          * @param {Roo.EventObject} e
51546          */
51547         "headerclick" : true,
51548         /**
51549          * @event headerdblclick
51550          * Fires when a header cell is double clicked
51551          * @param {Grid} this
51552          * @param {Number} columnIndex
51553          * @param {Roo.EventObject} e
51554          */
51555         "headerdblclick" : true,
51556         /**
51557          * @event rowcontextmenu
51558          * Fires when a row is right clicked
51559          * @param {Grid} this
51560          * @param {Number} rowIndex
51561          * @param {Roo.EventObject} e
51562          */
51563         "rowcontextmenu" : true,
51564         /**
51565          * @event cellcontextmenu
51566          * Fires when a cell is right clicked
51567          * @param {Grid} this
51568          * @param {Number} rowIndex
51569          * @param {Number} cellIndex
51570          * @param {Roo.EventObject} e
51571          */
51572          "cellcontextmenu" : true,
51573         /**
51574          * @event headercontextmenu
51575          * Fires when a header is right clicked
51576          * @param {Grid} this
51577          * @param {Number} columnIndex
51578          * @param {Roo.EventObject} e
51579          */
51580         "headercontextmenu" : true,
51581         /**
51582          * @event bodyscroll
51583          * Fires when the body element is scrolled
51584          * @param {Number} scrollLeft
51585          * @param {Number} scrollTop
51586          */
51587         "bodyscroll" : true,
51588         /**
51589          * @event columnresize
51590          * Fires when the user resizes a column
51591          * @param {Number} columnIndex
51592          * @param {Number} newSize
51593          */
51594         "columnresize" : true,
51595         /**
51596          * @event columnmove
51597          * Fires when the user moves a column
51598          * @param {Number} oldIndex
51599          * @param {Number} newIndex
51600          */
51601         "columnmove" : true,
51602         /**
51603          * @event startdrag
51604          * Fires when row(s) start being dragged
51605          * @param {Grid} this
51606          * @param {Roo.GridDD} dd The drag drop object
51607          * @param {event} e The raw browser event
51608          */
51609         "startdrag" : true,
51610         /**
51611          * @event enddrag
51612          * Fires when a drag operation is complete
51613          * @param {Grid} this
51614          * @param {Roo.GridDD} dd The drag drop object
51615          * @param {event} e The raw browser event
51616          */
51617         "enddrag" : true,
51618         /**
51619          * @event dragdrop
51620          * Fires when dragged row(s) are dropped on a valid DD target
51621          * @param {Grid} this
51622          * @param {Roo.GridDD} dd The drag drop object
51623          * @param {String} targetId The target drag drop object
51624          * @param {event} e The raw browser event
51625          */
51626         "dragdrop" : true,
51627         /**
51628          * @event dragover
51629          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51630          * @param {Grid} this
51631          * @param {Roo.GridDD} dd The drag drop object
51632          * @param {String} targetId The target drag drop object
51633          * @param {event} e The raw browser event
51634          */
51635         "dragover" : true,
51636         /**
51637          * @event dragenter
51638          *  Fires when the dragged row(s) first cross another DD target while being dragged
51639          * @param {Grid} this
51640          * @param {Roo.GridDD} dd The drag drop object
51641          * @param {String} targetId The target drag drop object
51642          * @param {event} e The raw browser event
51643          */
51644         "dragenter" : true,
51645         /**
51646          * @event dragout
51647          * Fires when the dragged row(s) leave another DD target while being dragged
51648          * @param {Grid} this
51649          * @param {Roo.GridDD} dd The drag drop object
51650          * @param {String} targetId The target drag drop object
51651          * @param {event} e The raw browser event
51652          */
51653         "dragout" : true,
51654         /**
51655          * @event rowclass
51656          * Fires when a row is rendered, so you can change add a style to it.
51657          * @param {GridView} gridview   The grid view
51658          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51659          */
51660         'rowclass' : true,
51661
51662         /**
51663          * @event render
51664          * Fires when the grid is rendered
51665          * @param {Grid} grid
51666          */
51667         'render' : true
51668     });
51669
51670     Roo.grid.Grid.superclass.constructor.call(this);
51671 };
51672 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51673     
51674     /**
51675      * @cfg {String} ddGroup - drag drop group.
51676      */
51677
51678     /**
51679      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51680      */
51681     minColumnWidth : 25,
51682
51683     /**
51684      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51685      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51686      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51687      */
51688     autoSizeColumns : false,
51689
51690     /**
51691      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51692      */
51693     autoSizeHeaders : true,
51694
51695     /**
51696      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51697      */
51698     monitorWindowResize : true,
51699
51700     /**
51701      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51702      * rows measured to get a columns size. Default is 0 (all rows).
51703      */
51704     maxRowsToMeasure : 0,
51705
51706     /**
51707      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51708      */
51709     trackMouseOver : true,
51710
51711     /**
51712     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51713     */
51714     
51715     /**
51716     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51717     */
51718     enableDragDrop : false,
51719     
51720     /**
51721     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51722     */
51723     enableColumnMove : true,
51724     
51725     /**
51726     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51727     */
51728     enableColumnHide : true,
51729     
51730     /**
51731     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51732     */
51733     enableRowHeightSync : false,
51734     
51735     /**
51736     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51737     */
51738     stripeRows : true,
51739     
51740     /**
51741     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51742     */
51743     autoHeight : false,
51744
51745     /**
51746      * @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.
51747      */
51748     autoExpandColumn : false,
51749
51750     /**
51751     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51752     * Default is 50.
51753     */
51754     autoExpandMin : 50,
51755
51756     /**
51757     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51758     */
51759     autoExpandMax : 1000,
51760
51761     /**
51762     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51763     */
51764     view : null,
51765
51766     /**
51767     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51768     */
51769     loadMask : false,
51770     /**
51771     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51772     */
51773     dropTarget: false,
51774     
51775    
51776     
51777     // private
51778     rendered : false,
51779
51780     /**
51781     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51782     * of a fixed width. Default is false.
51783     */
51784     /**
51785     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51786     */
51787     /**
51788      * Called once after all setup has been completed and the grid is ready to be rendered.
51789      * @return {Roo.grid.Grid} this
51790      */
51791     render : function()
51792     {
51793         var c = this.container;
51794         // try to detect autoHeight/width mode
51795         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51796             this.autoHeight = true;
51797         }
51798         var view = this.getView();
51799         view.init(this);
51800
51801         c.on("click", this.onClick, this);
51802         c.on("dblclick", this.onDblClick, this);
51803         c.on("contextmenu", this.onContextMenu, this);
51804         c.on("keydown", this.onKeyDown, this);
51805         if (Roo.isTouch) {
51806             c.on("touchstart", this.onTouchStart, this);
51807         }
51808
51809         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51810
51811         this.getSelectionModel().init(this);
51812
51813         view.render();
51814
51815         if(this.loadMask){
51816             this.loadMask = new Roo.LoadMask(this.container,
51817                     Roo.apply({store:this.dataSource}, this.loadMask));
51818         }
51819         
51820         
51821         if (this.toolbar && this.toolbar.xtype) {
51822             this.toolbar.container = this.getView().getHeaderPanel(true);
51823             this.toolbar = new Roo.Toolbar(this.toolbar);
51824         }
51825         if (this.footer && this.footer.xtype) {
51826             this.footer.dataSource = this.getDataSource();
51827             this.footer.container = this.getView().getFooterPanel(true);
51828             this.footer = Roo.factory(this.footer, Roo);
51829         }
51830         if (this.dropTarget && this.dropTarget.xtype) {
51831             delete this.dropTarget.xtype;
51832             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51833         }
51834         
51835         
51836         this.rendered = true;
51837         this.fireEvent('render', this);
51838         return this;
51839     },
51840
51841         /**
51842          * Reconfigures the grid to use a different Store and Column Model.
51843          * The View will be bound to the new objects and refreshed.
51844          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51845          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51846          */
51847     reconfigure : function(dataSource, colModel){
51848         if(this.loadMask){
51849             this.loadMask.destroy();
51850             this.loadMask = new Roo.LoadMask(this.container,
51851                     Roo.apply({store:dataSource}, this.loadMask));
51852         }
51853         this.view.bind(dataSource, colModel);
51854         this.dataSource = dataSource;
51855         this.colModel = colModel;
51856         this.view.refresh(true);
51857     },
51858
51859     // private
51860     onKeyDown : function(e){
51861         this.fireEvent("keydown", e);
51862     },
51863
51864     /**
51865      * Destroy this grid.
51866      * @param {Boolean} removeEl True to remove the element
51867      */
51868     destroy : function(removeEl, keepListeners){
51869         if(this.loadMask){
51870             this.loadMask.destroy();
51871         }
51872         var c = this.container;
51873         c.removeAllListeners();
51874         this.view.destroy();
51875         this.colModel.purgeListeners();
51876         if(!keepListeners){
51877             this.purgeListeners();
51878         }
51879         c.update("");
51880         if(removeEl === true){
51881             c.remove();
51882         }
51883     },
51884
51885     // private
51886     processEvent : function(name, e){
51887         // does this fire select???
51888         Roo.log('grid:processEvent '  + name);
51889         
51890         if (name != 'touchstart' ) {
51891             this.fireEvent(name, e);    
51892         }
51893         
51894         var t = e.getTarget();
51895         var v = this.view;
51896         var header = v.findHeaderIndex(t);
51897         if(header !== false){
51898             var ename = name == 'touchstart' ? 'click' : name;
51899              
51900             this.fireEvent("header" + ename, this, header, e);
51901         }else{
51902             var row = v.findRowIndex(t);
51903             var cell = v.findCellIndex(t);
51904             if (name == 'touchstart') {
51905                 // first touch is always a click.
51906                 // hopefull this happens after selection is updated.?
51907                 name = false;
51908                 
51909                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51910                     var cs = this.selModel.getSelectedCell();
51911                     if (row == cs[0] && cell == cs[1]){
51912                         name = 'dblclick';
51913                     }
51914                 }
51915                 if (typeof(this.selModel.getSelections) != 'undefined') {
51916                     var cs = this.selModel.getSelections();
51917                     var ds = this.dataSource;
51918                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51919                         name = 'dblclick';
51920                     }
51921                 }
51922                 if (!name) {
51923                     return;
51924                 }
51925             }
51926             
51927             
51928             if(row !== false){
51929                 this.fireEvent("row" + name, this, row, e);
51930                 if(cell !== false){
51931                     this.fireEvent("cell" + name, this, row, cell, e);
51932                 }
51933             }
51934         }
51935     },
51936
51937     // private
51938     onClick : function(e){
51939         this.processEvent("click", e);
51940     },
51941    // private
51942     onTouchStart : function(e){
51943         this.processEvent("touchstart", e);
51944     },
51945
51946     // private
51947     onContextMenu : function(e, t){
51948         this.processEvent("contextmenu", e);
51949     },
51950
51951     // private
51952     onDblClick : function(e){
51953         this.processEvent("dblclick", e);
51954     },
51955
51956     // private
51957     walkCells : function(row, col, step, fn, scope){
51958         var cm = this.colModel, clen = cm.getColumnCount();
51959         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51960         if(step < 0){
51961             if(col < 0){
51962                 row--;
51963                 first = false;
51964             }
51965             while(row >= 0){
51966                 if(!first){
51967                     col = clen-1;
51968                 }
51969                 first = false;
51970                 while(col >= 0){
51971                     if(fn.call(scope || this, row, col, cm) === true){
51972                         return [row, col];
51973                     }
51974                     col--;
51975                 }
51976                 row--;
51977             }
51978         } else {
51979             if(col >= clen){
51980                 row++;
51981                 first = false;
51982             }
51983             while(row < rlen){
51984                 if(!first){
51985                     col = 0;
51986                 }
51987                 first = false;
51988                 while(col < clen){
51989                     if(fn.call(scope || this, row, col, cm) === true){
51990                         return [row, col];
51991                     }
51992                     col++;
51993                 }
51994                 row++;
51995             }
51996         }
51997         return null;
51998     },
51999
52000     // private
52001     getSelections : function(){
52002         return this.selModel.getSelections();
52003     },
52004
52005     /**
52006      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52007      * but if manual update is required this method will initiate it.
52008      */
52009     autoSize : function(){
52010         if(this.rendered){
52011             this.view.layout();
52012             if(this.view.adjustForScroll){
52013                 this.view.adjustForScroll();
52014             }
52015         }
52016     },
52017
52018     /**
52019      * Returns the grid's underlying element.
52020      * @return {Element} The element
52021      */
52022     getGridEl : function(){
52023         return this.container;
52024     },
52025
52026     // private for compatibility, overridden by editor grid
52027     stopEditing : function(){},
52028
52029     /**
52030      * Returns the grid's SelectionModel.
52031      * @return {SelectionModel}
52032      */
52033     getSelectionModel : function(){
52034         if(!this.selModel){
52035             this.selModel = new Roo.grid.RowSelectionModel();
52036         }
52037         return this.selModel;
52038     },
52039
52040     /**
52041      * Returns the grid's DataSource.
52042      * @return {DataSource}
52043      */
52044     getDataSource : function(){
52045         return this.dataSource;
52046     },
52047
52048     /**
52049      * Returns the grid's ColumnModel.
52050      * @return {ColumnModel}
52051      */
52052     getColumnModel : function(){
52053         return this.colModel;
52054     },
52055
52056     /**
52057      * Returns the grid's GridView object.
52058      * @return {GridView}
52059      */
52060     getView : function(){
52061         if(!this.view){
52062             this.view = new Roo.grid.GridView(this.viewConfig);
52063         }
52064         return this.view;
52065     },
52066     /**
52067      * Called to get grid's drag proxy text, by default returns this.ddText.
52068      * @return {String}
52069      */
52070     getDragDropText : function(){
52071         var count = this.selModel.getCount();
52072         return String.format(this.ddText, count, count == 1 ? '' : 's');
52073     }
52074 });
52075 /**
52076  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52077  * %0 is replaced with the number of selected rows.
52078  * @type String
52079  */
52080 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52081  * Based on:
52082  * Ext JS Library 1.1.1
52083  * Copyright(c) 2006-2007, Ext JS, LLC.
52084  *
52085  * Originally Released Under LGPL - original licence link has changed is not relivant.
52086  *
52087  * Fork - LGPL
52088  * <script type="text/javascript">
52089  */
52090  
52091 Roo.grid.AbstractGridView = function(){
52092         this.grid = null;
52093         
52094         this.events = {
52095             "beforerowremoved" : true,
52096             "beforerowsinserted" : true,
52097             "beforerefresh" : true,
52098             "rowremoved" : true,
52099             "rowsinserted" : true,
52100             "rowupdated" : true,
52101             "refresh" : true
52102         };
52103     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52104 };
52105
52106 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52107     rowClass : "x-grid-row",
52108     cellClass : "x-grid-cell",
52109     tdClass : "x-grid-td",
52110     hdClass : "x-grid-hd",
52111     splitClass : "x-grid-hd-split",
52112     
52113         init: function(grid){
52114         this.grid = grid;
52115                 var cid = this.grid.getGridEl().id;
52116         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52117         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52118         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52119         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52120         },
52121         
52122         getColumnRenderers : function(){
52123         var renderers = [];
52124         var cm = this.grid.colModel;
52125         var colCount = cm.getColumnCount();
52126         for(var i = 0; i < colCount; i++){
52127             renderers[i] = cm.getRenderer(i);
52128         }
52129         return renderers;
52130     },
52131     
52132     getColumnIds : function(){
52133         var ids = [];
52134         var cm = this.grid.colModel;
52135         var colCount = cm.getColumnCount();
52136         for(var i = 0; i < colCount; i++){
52137             ids[i] = cm.getColumnId(i);
52138         }
52139         return ids;
52140     },
52141     
52142     getDataIndexes : function(){
52143         if(!this.indexMap){
52144             this.indexMap = this.buildIndexMap();
52145         }
52146         return this.indexMap.colToData;
52147     },
52148     
52149     getColumnIndexByDataIndex : function(dataIndex){
52150         if(!this.indexMap){
52151             this.indexMap = this.buildIndexMap();
52152         }
52153         return this.indexMap.dataToCol[dataIndex];
52154     },
52155     
52156     /**
52157      * Set a css style for a column dynamically. 
52158      * @param {Number} colIndex The index of the column
52159      * @param {String} name The css property name
52160      * @param {String} value The css value
52161      */
52162     setCSSStyle : function(colIndex, name, value){
52163         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52164         Roo.util.CSS.updateRule(selector, name, value);
52165     },
52166     
52167     generateRules : function(cm){
52168         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52169         Roo.util.CSS.removeStyleSheet(rulesId);
52170         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52171             var cid = cm.getColumnId(i);
52172             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52173                          this.tdSelector, cid, " {\n}\n",
52174                          this.hdSelector, cid, " {\n}\n",
52175                          this.splitSelector, cid, " {\n}\n");
52176         }
52177         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52178     }
52179 });/*
52180  * Based on:
52181  * Ext JS Library 1.1.1
52182  * Copyright(c) 2006-2007, Ext JS, LLC.
52183  *
52184  * Originally Released Under LGPL - original licence link has changed is not relivant.
52185  *
52186  * Fork - LGPL
52187  * <script type="text/javascript">
52188  */
52189
52190 // private
52191 // This is a support class used internally by the Grid components
52192 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52193     this.grid = grid;
52194     this.view = grid.getView();
52195     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52196     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52197     if(hd2){
52198         this.setHandleElId(Roo.id(hd));
52199         this.setOuterHandleElId(Roo.id(hd2));
52200     }
52201     this.scroll = false;
52202 };
52203 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52204     maxDragWidth: 120,
52205     getDragData : function(e){
52206         var t = Roo.lib.Event.getTarget(e);
52207         var h = this.view.findHeaderCell(t);
52208         if(h){
52209             return {ddel: h.firstChild, header:h};
52210         }
52211         return false;
52212     },
52213
52214     onInitDrag : function(e){
52215         this.view.headersDisabled = true;
52216         var clone = this.dragData.ddel.cloneNode(true);
52217         clone.id = Roo.id();
52218         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52219         this.proxy.update(clone);
52220         return true;
52221     },
52222
52223     afterValidDrop : function(){
52224         var v = this.view;
52225         setTimeout(function(){
52226             v.headersDisabled = false;
52227         }, 50);
52228     },
52229
52230     afterInvalidDrop : function(){
52231         var v = this.view;
52232         setTimeout(function(){
52233             v.headersDisabled = false;
52234         }, 50);
52235     }
52236 });
52237 /*
52238  * Based on:
52239  * Ext JS Library 1.1.1
52240  * Copyright(c) 2006-2007, Ext JS, LLC.
52241  *
52242  * Originally Released Under LGPL - original licence link has changed is not relivant.
52243  *
52244  * Fork - LGPL
52245  * <script type="text/javascript">
52246  */
52247 // private
52248 // This is a support class used internally by the Grid components
52249 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52250     this.grid = grid;
52251     this.view = grid.getView();
52252     // split the proxies so they don't interfere with mouse events
52253     this.proxyTop = Roo.DomHelper.append(document.body, {
52254         cls:"col-move-top", html:"&#160;"
52255     }, true);
52256     this.proxyBottom = Roo.DomHelper.append(document.body, {
52257         cls:"col-move-bottom", html:"&#160;"
52258     }, true);
52259     this.proxyTop.hide = this.proxyBottom.hide = function(){
52260         this.setLeftTop(-100,-100);
52261         this.setStyle("visibility", "hidden");
52262     };
52263     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52264     // temporarily disabled
52265     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52266     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52267 };
52268 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52269     proxyOffsets : [-4, -9],
52270     fly: Roo.Element.fly,
52271
52272     getTargetFromEvent : function(e){
52273         var t = Roo.lib.Event.getTarget(e);
52274         var cindex = this.view.findCellIndex(t);
52275         if(cindex !== false){
52276             return this.view.getHeaderCell(cindex);
52277         }
52278         return null;
52279     },
52280
52281     nextVisible : function(h){
52282         var v = this.view, cm = this.grid.colModel;
52283         h = h.nextSibling;
52284         while(h){
52285             if(!cm.isHidden(v.getCellIndex(h))){
52286                 return h;
52287             }
52288             h = h.nextSibling;
52289         }
52290         return null;
52291     },
52292
52293     prevVisible : function(h){
52294         var v = this.view, cm = this.grid.colModel;
52295         h = h.prevSibling;
52296         while(h){
52297             if(!cm.isHidden(v.getCellIndex(h))){
52298                 return h;
52299             }
52300             h = h.prevSibling;
52301         }
52302         return null;
52303     },
52304
52305     positionIndicator : function(h, n, e){
52306         var x = Roo.lib.Event.getPageX(e);
52307         var r = Roo.lib.Dom.getRegion(n.firstChild);
52308         var px, pt, py = r.top + this.proxyOffsets[1];
52309         if((r.right - x) <= (r.right-r.left)/2){
52310             px = r.right+this.view.borderWidth;
52311             pt = "after";
52312         }else{
52313             px = r.left;
52314             pt = "before";
52315         }
52316         var oldIndex = this.view.getCellIndex(h);
52317         var newIndex = this.view.getCellIndex(n);
52318
52319         if(this.grid.colModel.isFixed(newIndex)){
52320             return false;
52321         }
52322
52323         var locked = this.grid.colModel.isLocked(newIndex);
52324
52325         if(pt == "after"){
52326             newIndex++;
52327         }
52328         if(oldIndex < newIndex){
52329             newIndex--;
52330         }
52331         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52332             return false;
52333         }
52334         px +=  this.proxyOffsets[0];
52335         this.proxyTop.setLeftTop(px, py);
52336         this.proxyTop.show();
52337         if(!this.bottomOffset){
52338             this.bottomOffset = this.view.mainHd.getHeight();
52339         }
52340         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52341         this.proxyBottom.show();
52342         return pt;
52343     },
52344
52345     onNodeEnter : function(n, dd, e, data){
52346         if(data.header != n){
52347             this.positionIndicator(data.header, n, e);
52348         }
52349     },
52350
52351     onNodeOver : function(n, dd, e, data){
52352         var result = false;
52353         if(data.header != n){
52354             result = this.positionIndicator(data.header, n, e);
52355         }
52356         if(!result){
52357             this.proxyTop.hide();
52358             this.proxyBottom.hide();
52359         }
52360         return result ? this.dropAllowed : this.dropNotAllowed;
52361     },
52362
52363     onNodeOut : function(n, dd, e, data){
52364         this.proxyTop.hide();
52365         this.proxyBottom.hide();
52366     },
52367
52368     onNodeDrop : function(n, dd, e, data){
52369         var h = data.header;
52370         if(h != n){
52371             var cm = this.grid.colModel;
52372             var x = Roo.lib.Event.getPageX(e);
52373             var r = Roo.lib.Dom.getRegion(n.firstChild);
52374             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52375             var oldIndex = this.view.getCellIndex(h);
52376             var newIndex = this.view.getCellIndex(n);
52377             var locked = cm.isLocked(newIndex);
52378             if(pt == "after"){
52379                 newIndex++;
52380             }
52381             if(oldIndex < newIndex){
52382                 newIndex--;
52383             }
52384             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52385                 return false;
52386             }
52387             cm.setLocked(oldIndex, locked, true);
52388             cm.moveColumn(oldIndex, newIndex);
52389             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52390             return true;
52391         }
52392         return false;
52393     }
52394 });
52395 /*
52396  * Based on:
52397  * Ext JS Library 1.1.1
52398  * Copyright(c) 2006-2007, Ext JS, LLC.
52399  *
52400  * Originally Released Under LGPL - original licence link has changed is not relivant.
52401  *
52402  * Fork - LGPL
52403  * <script type="text/javascript">
52404  */
52405   
52406 /**
52407  * @class Roo.grid.GridView
52408  * @extends Roo.util.Observable
52409  *
52410  * @constructor
52411  * @param {Object} config
52412  */
52413 Roo.grid.GridView = function(config){
52414     Roo.grid.GridView.superclass.constructor.call(this);
52415     this.el = null;
52416
52417     Roo.apply(this, config);
52418 };
52419
52420 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52421
52422     unselectable :  'unselectable="on"',
52423     unselectableCls :  'x-unselectable',
52424     
52425     
52426     rowClass : "x-grid-row",
52427
52428     cellClass : "x-grid-col",
52429
52430     tdClass : "x-grid-td",
52431
52432     hdClass : "x-grid-hd",
52433
52434     splitClass : "x-grid-split",
52435
52436     sortClasses : ["sort-asc", "sort-desc"],
52437
52438     enableMoveAnim : false,
52439
52440     hlColor: "C3DAF9",
52441
52442     dh : Roo.DomHelper,
52443
52444     fly : Roo.Element.fly,
52445
52446     css : Roo.util.CSS,
52447
52448     borderWidth: 1,
52449
52450     splitOffset: 3,
52451
52452     scrollIncrement : 22,
52453
52454     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52455
52456     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52457
52458     bind : function(ds, cm){
52459         if(this.ds){
52460             this.ds.un("load", this.onLoad, this);
52461             this.ds.un("datachanged", this.onDataChange, this);
52462             this.ds.un("add", this.onAdd, this);
52463             this.ds.un("remove", this.onRemove, this);
52464             this.ds.un("update", this.onUpdate, this);
52465             this.ds.un("clear", this.onClear, this);
52466         }
52467         if(ds){
52468             ds.on("load", this.onLoad, this);
52469             ds.on("datachanged", this.onDataChange, this);
52470             ds.on("add", this.onAdd, this);
52471             ds.on("remove", this.onRemove, this);
52472             ds.on("update", this.onUpdate, this);
52473             ds.on("clear", this.onClear, this);
52474         }
52475         this.ds = ds;
52476
52477         if(this.cm){
52478             this.cm.un("widthchange", this.onColWidthChange, this);
52479             this.cm.un("headerchange", this.onHeaderChange, this);
52480             this.cm.un("hiddenchange", this.onHiddenChange, this);
52481             this.cm.un("columnmoved", this.onColumnMove, this);
52482             this.cm.un("columnlockchange", this.onColumnLock, this);
52483         }
52484         if(cm){
52485             this.generateRules(cm);
52486             cm.on("widthchange", this.onColWidthChange, this);
52487             cm.on("headerchange", this.onHeaderChange, this);
52488             cm.on("hiddenchange", this.onHiddenChange, this);
52489             cm.on("columnmoved", this.onColumnMove, this);
52490             cm.on("columnlockchange", this.onColumnLock, this);
52491         }
52492         this.cm = cm;
52493     },
52494
52495     init: function(grid){
52496         Roo.grid.GridView.superclass.init.call(this, grid);
52497
52498         this.bind(grid.dataSource, grid.colModel);
52499
52500         grid.on("headerclick", this.handleHeaderClick, this);
52501
52502         if(grid.trackMouseOver){
52503             grid.on("mouseover", this.onRowOver, this);
52504             grid.on("mouseout", this.onRowOut, this);
52505         }
52506         grid.cancelTextSelection = function(){};
52507         this.gridId = grid.id;
52508
52509         var tpls = this.templates || {};
52510
52511         if(!tpls.master){
52512             tpls.master = new Roo.Template(
52513                '<div class="x-grid" hidefocus="true">',
52514                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52515                   '<div class="x-grid-topbar"></div>',
52516                   '<div class="x-grid-scroller"><div></div></div>',
52517                   '<div class="x-grid-locked">',
52518                       '<div class="x-grid-header">{lockedHeader}</div>',
52519                       '<div class="x-grid-body">{lockedBody}</div>',
52520                   "</div>",
52521                   '<div class="x-grid-viewport">',
52522                       '<div class="x-grid-header">{header}</div>',
52523                       '<div class="x-grid-body">{body}</div>',
52524                   "</div>",
52525                   '<div class="x-grid-bottombar"></div>',
52526                  
52527                   '<div class="x-grid-resize-proxy">&#160;</div>',
52528                "</div>"
52529             );
52530             tpls.master.disableformats = true;
52531         }
52532
52533         if(!tpls.header){
52534             tpls.header = new Roo.Template(
52535                '<table border="0" cellspacing="0" cellpadding="0">',
52536                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52537                "</table>{splits}"
52538             );
52539             tpls.header.disableformats = true;
52540         }
52541         tpls.header.compile();
52542
52543         if(!tpls.hcell){
52544             tpls.hcell = new Roo.Template(
52545                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52546                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52547                 "</div></td>"
52548              );
52549              tpls.hcell.disableFormats = true;
52550         }
52551         tpls.hcell.compile();
52552
52553         if(!tpls.hsplit){
52554             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52555                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52556             tpls.hsplit.disableFormats = true;
52557         }
52558         tpls.hsplit.compile();
52559
52560         if(!tpls.body){
52561             tpls.body = new Roo.Template(
52562                '<table border="0" cellspacing="0" cellpadding="0">',
52563                "<tbody>{rows}</tbody>",
52564                "</table>"
52565             );
52566             tpls.body.disableFormats = true;
52567         }
52568         tpls.body.compile();
52569
52570         if(!tpls.row){
52571             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52572             tpls.row.disableFormats = true;
52573         }
52574         tpls.row.compile();
52575
52576         if(!tpls.cell){
52577             tpls.cell = new Roo.Template(
52578                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52579                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52580                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52581                 "</td>"
52582             );
52583             tpls.cell.disableFormats = true;
52584         }
52585         tpls.cell.compile();
52586
52587         this.templates = tpls;
52588     },
52589
52590     // remap these for backwards compat
52591     onColWidthChange : function(){
52592         this.updateColumns.apply(this, arguments);
52593     },
52594     onHeaderChange : function(){
52595         this.updateHeaders.apply(this, arguments);
52596     }, 
52597     onHiddenChange : function(){
52598         this.handleHiddenChange.apply(this, arguments);
52599     },
52600     onColumnMove : function(){
52601         this.handleColumnMove.apply(this, arguments);
52602     },
52603     onColumnLock : function(){
52604         this.handleLockChange.apply(this, arguments);
52605     },
52606
52607     onDataChange : function(){
52608         this.refresh();
52609         this.updateHeaderSortState();
52610     },
52611
52612     onClear : function(){
52613         this.refresh();
52614     },
52615
52616     onUpdate : function(ds, record){
52617         this.refreshRow(record);
52618     },
52619
52620     refreshRow : function(record){
52621         var ds = this.ds, index;
52622         if(typeof record == 'number'){
52623             index = record;
52624             record = ds.getAt(index);
52625         }else{
52626             index = ds.indexOf(record);
52627         }
52628         this.insertRows(ds, index, index, true);
52629         this.onRemove(ds, record, index+1, true);
52630         this.syncRowHeights(index, index);
52631         this.layout();
52632         this.fireEvent("rowupdated", this, index, record);
52633     },
52634
52635     onAdd : function(ds, records, index){
52636         this.insertRows(ds, index, index + (records.length-1));
52637     },
52638
52639     onRemove : function(ds, record, index, isUpdate){
52640         if(isUpdate !== true){
52641             this.fireEvent("beforerowremoved", this, index, record);
52642         }
52643         var bt = this.getBodyTable(), lt = this.getLockedTable();
52644         if(bt.rows[index]){
52645             bt.firstChild.removeChild(bt.rows[index]);
52646         }
52647         if(lt.rows[index]){
52648             lt.firstChild.removeChild(lt.rows[index]);
52649         }
52650         if(isUpdate !== true){
52651             this.stripeRows(index);
52652             this.syncRowHeights(index, index);
52653             this.layout();
52654             this.fireEvent("rowremoved", this, index, record);
52655         }
52656     },
52657
52658     onLoad : function(){
52659         this.scrollToTop();
52660     },
52661
52662     /**
52663      * Scrolls the grid to the top
52664      */
52665     scrollToTop : function(){
52666         if(this.scroller){
52667             this.scroller.dom.scrollTop = 0;
52668             this.syncScroll();
52669         }
52670     },
52671
52672     /**
52673      * Gets a panel in the header of the grid that can be used for toolbars etc.
52674      * After modifying the contents of this panel a call to grid.autoSize() may be
52675      * required to register any changes in size.
52676      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52677      * @return Roo.Element
52678      */
52679     getHeaderPanel : function(doShow){
52680         if(doShow){
52681             this.headerPanel.show();
52682         }
52683         return this.headerPanel;
52684     },
52685
52686     /**
52687      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52688      * After modifying the contents of this panel a call to grid.autoSize() may be
52689      * required to register any changes in size.
52690      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52691      * @return Roo.Element
52692      */
52693     getFooterPanel : function(doShow){
52694         if(doShow){
52695             this.footerPanel.show();
52696         }
52697         return this.footerPanel;
52698     },
52699
52700     initElements : function(){
52701         var E = Roo.Element;
52702         var el = this.grid.getGridEl().dom.firstChild;
52703         var cs = el.childNodes;
52704
52705         this.el = new E(el);
52706         
52707          this.focusEl = new E(el.firstChild);
52708         this.focusEl.swallowEvent("click", true);
52709         
52710         this.headerPanel = new E(cs[1]);
52711         this.headerPanel.enableDisplayMode("block");
52712
52713         this.scroller = new E(cs[2]);
52714         this.scrollSizer = new E(this.scroller.dom.firstChild);
52715
52716         this.lockedWrap = new E(cs[3]);
52717         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52718         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52719
52720         this.mainWrap = new E(cs[4]);
52721         this.mainHd = new E(this.mainWrap.dom.firstChild);
52722         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52723
52724         this.footerPanel = new E(cs[5]);
52725         this.footerPanel.enableDisplayMode("block");
52726
52727         this.resizeProxy = new E(cs[6]);
52728
52729         this.headerSelector = String.format(
52730            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52731            this.lockedHd.id, this.mainHd.id
52732         );
52733
52734         this.splitterSelector = String.format(
52735            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52736            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52737         );
52738     },
52739     idToCssName : function(s)
52740     {
52741         return s.replace(/[^a-z0-9]+/ig, '-');
52742     },
52743
52744     getHeaderCell : function(index){
52745         return Roo.DomQuery.select(this.headerSelector)[index];
52746     },
52747
52748     getHeaderCellMeasure : function(index){
52749         return this.getHeaderCell(index).firstChild;
52750     },
52751
52752     getHeaderCellText : function(index){
52753         return this.getHeaderCell(index).firstChild.firstChild;
52754     },
52755
52756     getLockedTable : function(){
52757         return this.lockedBody.dom.firstChild;
52758     },
52759
52760     getBodyTable : function(){
52761         return this.mainBody.dom.firstChild;
52762     },
52763
52764     getLockedRow : function(index){
52765         return this.getLockedTable().rows[index];
52766     },
52767
52768     getRow : function(index){
52769         return this.getBodyTable().rows[index];
52770     },
52771
52772     getRowComposite : function(index){
52773         if(!this.rowEl){
52774             this.rowEl = new Roo.CompositeElementLite();
52775         }
52776         var els = [], lrow, mrow;
52777         if(lrow = this.getLockedRow(index)){
52778             els.push(lrow);
52779         }
52780         if(mrow = this.getRow(index)){
52781             els.push(mrow);
52782         }
52783         this.rowEl.elements = els;
52784         return this.rowEl;
52785     },
52786     /**
52787      * Gets the 'td' of the cell
52788      * 
52789      * @param {Integer} rowIndex row to select
52790      * @param {Integer} colIndex column to select
52791      * 
52792      * @return {Object} 
52793      */
52794     getCell : function(rowIndex, colIndex){
52795         var locked = this.cm.getLockedCount();
52796         var source;
52797         if(colIndex < locked){
52798             source = this.lockedBody.dom.firstChild;
52799         }else{
52800             source = this.mainBody.dom.firstChild;
52801             colIndex -= locked;
52802         }
52803         return source.rows[rowIndex].childNodes[colIndex];
52804     },
52805
52806     getCellText : function(rowIndex, colIndex){
52807         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52808     },
52809
52810     getCellBox : function(cell){
52811         var b = this.fly(cell).getBox();
52812         if(Roo.isOpera){ // opera fails to report the Y
52813             b.y = cell.offsetTop + this.mainBody.getY();
52814         }
52815         return b;
52816     },
52817
52818     getCellIndex : function(cell){
52819         var id = String(cell.className).match(this.cellRE);
52820         if(id){
52821             return parseInt(id[1], 10);
52822         }
52823         return 0;
52824     },
52825
52826     findHeaderIndex : function(n){
52827         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52828         return r ? this.getCellIndex(r) : false;
52829     },
52830
52831     findHeaderCell : function(n){
52832         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52833         return r ? r : false;
52834     },
52835
52836     findRowIndex : function(n){
52837         if(!n){
52838             return false;
52839         }
52840         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52841         return r ? r.rowIndex : false;
52842     },
52843
52844     findCellIndex : function(node){
52845         var stop = this.el.dom;
52846         while(node && node != stop){
52847             if(this.findRE.test(node.className)){
52848                 return this.getCellIndex(node);
52849             }
52850             node = node.parentNode;
52851         }
52852         return false;
52853     },
52854
52855     getColumnId : function(index){
52856         return this.cm.getColumnId(index);
52857     },
52858
52859     getSplitters : function()
52860     {
52861         if(this.splitterSelector){
52862            return Roo.DomQuery.select(this.splitterSelector);
52863         }else{
52864             return null;
52865       }
52866     },
52867
52868     getSplitter : function(index){
52869         return this.getSplitters()[index];
52870     },
52871
52872     onRowOver : function(e, t){
52873         var row;
52874         if((row = this.findRowIndex(t)) !== false){
52875             this.getRowComposite(row).addClass("x-grid-row-over");
52876         }
52877     },
52878
52879     onRowOut : function(e, t){
52880         var row;
52881         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52882             this.getRowComposite(row).removeClass("x-grid-row-over");
52883         }
52884     },
52885
52886     renderHeaders : function(){
52887         var cm = this.cm;
52888         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52889         var cb = [], lb = [], sb = [], lsb = [], p = {};
52890         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52891             p.cellId = "x-grid-hd-0-" + i;
52892             p.splitId = "x-grid-csplit-0-" + i;
52893             p.id = cm.getColumnId(i);
52894             p.title = cm.getColumnTooltip(i) || "";
52895             p.value = cm.getColumnHeader(i) || "";
52896             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52897             if(!cm.isLocked(i)){
52898                 cb[cb.length] = ct.apply(p);
52899                 sb[sb.length] = st.apply(p);
52900             }else{
52901                 lb[lb.length] = ct.apply(p);
52902                 lsb[lsb.length] = st.apply(p);
52903             }
52904         }
52905         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52906                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52907     },
52908
52909     updateHeaders : function(){
52910         var html = this.renderHeaders();
52911         this.lockedHd.update(html[0]);
52912         this.mainHd.update(html[1]);
52913     },
52914
52915     /**
52916      * Focuses the specified row.
52917      * @param {Number} row The row index
52918      */
52919     focusRow : function(row)
52920     {
52921         //Roo.log('GridView.focusRow');
52922         var x = this.scroller.dom.scrollLeft;
52923         this.focusCell(row, 0, false);
52924         this.scroller.dom.scrollLeft = x;
52925     },
52926
52927     /**
52928      * Focuses the specified cell.
52929      * @param {Number} row The row index
52930      * @param {Number} col The column index
52931      * @param {Boolean} hscroll false to disable horizontal scrolling
52932      */
52933     focusCell : function(row, col, hscroll)
52934     {
52935         //Roo.log('GridView.focusCell');
52936         var el = this.ensureVisible(row, col, hscroll);
52937         this.focusEl.alignTo(el, "tl-tl");
52938         if(Roo.isGecko){
52939             this.focusEl.focus();
52940         }else{
52941             this.focusEl.focus.defer(1, this.focusEl);
52942         }
52943     },
52944
52945     /**
52946      * Scrolls the specified cell into view
52947      * @param {Number} row The row index
52948      * @param {Number} col The column index
52949      * @param {Boolean} hscroll false to disable horizontal scrolling
52950      */
52951     ensureVisible : function(row, col, hscroll)
52952     {
52953         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52954         //return null; //disable for testing.
52955         if(typeof row != "number"){
52956             row = row.rowIndex;
52957         }
52958         if(row < 0 && row >= this.ds.getCount()){
52959             return  null;
52960         }
52961         col = (col !== undefined ? col : 0);
52962         var cm = this.grid.colModel;
52963         while(cm.isHidden(col)){
52964             col++;
52965         }
52966
52967         var el = this.getCell(row, col);
52968         if(!el){
52969             return null;
52970         }
52971         var c = this.scroller.dom;
52972
52973         var ctop = parseInt(el.offsetTop, 10);
52974         var cleft = parseInt(el.offsetLeft, 10);
52975         var cbot = ctop + el.offsetHeight;
52976         var cright = cleft + el.offsetWidth;
52977         
52978         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52979         var stop = parseInt(c.scrollTop, 10);
52980         var sleft = parseInt(c.scrollLeft, 10);
52981         var sbot = stop + ch;
52982         var sright = sleft + c.clientWidth;
52983         /*
52984         Roo.log('GridView.ensureVisible:' +
52985                 ' ctop:' + ctop +
52986                 ' c.clientHeight:' + c.clientHeight +
52987                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52988                 ' stop:' + stop +
52989                 ' cbot:' + cbot +
52990                 ' sbot:' + sbot +
52991                 ' ch:' + ch  
52992                 );
52993         */
52994         if(ctop < stop){
52995              c.scrollTop = ctop;
52996             //Roo.log("set scrolltop to ctop DISABLE?");
52997         }else if(cbot > sbot){
52998             //Roo.log("set scrolltop to cbot-ch");
52999             c.scrollTop = cbot-ch;
53000         }
53001         
53002         if(hscroll !== false){
53003             if(cleft < sleft){
53004                 c.scrollLeft = cleft;
53005             }else if(cright > sright){
53006                 c.scrollLeft = cright-c.clientWidth;
53007             }
53008         }
53009          
53010         return el;
53011     },
53012
53013     updateColumns : function(){
53014         this.grid.stopEditing();
53015         var cm = this.grid.colModel, colIds = this.getColumnIds();
53016         //var totalWidth = cm.getTotalWidth();
53017         var pos = 0;
53018         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53019             //if(cm.isHidden(i)) continue;
53020             var w = cm.getColumnWidth(i);
53021             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53022             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53023         }
53024         this.updateSplitters();
53025     },
53026
53027     generateRules : function(cm){
53028         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53029         Roo.util.CSS.removeStyleSheet(rulesId);
53030         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53031             var cid = cm.getColumnId(i);
53032             var align = '';
53033             if(cm.config[i].align){
53034                 align = 'text-align:'+cm.config[i].align+';';
53035             }
53036             var hidden = '';
53037             if(cm.isHidden(i)){
53038                 hidden = 'display:none;';
53039             }
53040             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53041             ruleBuf.push(
53042                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53043                     this.hdSelector, cid, " {\n", align, width, "}\n",
53044                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53045                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53046         }
53047         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53048     },
53049
53050     updateSplitters : function(){
53051         var cm = this.cm, s = this.getSplitters();
53052         if(s){ // splitters not created yet
53053             var pos = 0, locked = true;
53054             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53055                 if(cm.isHidden(i)) continue;
53056                 var w = cm.getColumnWidth(i); // make sure it's a number
53057                 if(!cm.isLocked(i) && locked){
53058                     pos = 0;
53059                     locked = false;
53060                 }
53061                 pos += w;
53062                 s[i].style.left = (pos-this.splitOffset) + "px";
53063             }
53064         }
53065     },
53066
53067     handleHiddenChange : function(colModel, colIndex, hidden){
53068         if(hidden){
53069             this.hideColumn(colIndex);
53070         }else{
53071             this.unhideColumn(colIndex);
53072         }
53073     },
53074
53075     hideColumn : function(colIndex){
53076         var cid = this.getColumnId(colIndex);
53077         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53078         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53079         if(Roo.isSafari){
53080             this.updateHeaders();
53081         }
53082         this.updateSplitters();
53083         this.layout();
53084     },
53085
53086     unhideColumn : function(colIndex){
53087         var cid = this.getColumnId(colIndex);
53088         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53089         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53090
53091         if(Roo.isSafari){
53092             this.updateHeaders();
53093         }
53094         this.updateSplitters();
53095         this.layout();
53096     },
53097
53098     insertRows : function(dm, firstRow, lastRow, isUpdate){
53099         if(firstRow == 0 && lastRow == dm.getCount()-1){
53100             this.refresh();
53101         }else{
53102             if(!isUpdate){
53103                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53104             }
53105             var s = this.getScrollState();
53106             var markup = this.renderRows(firstRow, lastRow);
53107             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53108             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53109             this.restoreScroll(s);
53110             if(!isUpdate){
53111                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53112                 this.syncRowHeights(firstRow, lastRow);
53113                 this.stripeRows(firstRow);
53114                 this.layout();
53115             }
53116         }
53117     },
53118
53119     bufferRows : function(markup, target, index){
53120         var before = null, trows = target.rows, tbody = target.tBodies[0];
53121         if(index < trows.length){
53122             before = trows[index];
53123         }
53124         var b = document.createElement("div");
53125         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53126         var rows = b.firstChild.rows;
53127         for(var i = 0, len = rows.length; i < len; i++){
53128             if(before){
53129                 tbody.insertBefore(rows[0], before);
53130             }else{
53131                 tbody.appendChild(rows[0]);
53132             }
53133         }
53134         b.innerHTML = "";
53135         b = null;
53136     },
53137
53138     deleteRows : function(dm, firstRow, lastRow){
53139         if(dm.getRowCount()<1){
53140             this.fireEvent("beforerefresh", this);
53141             this.mainBody.update("");
53142             this.lockedBody.update("");
53143             this.fireEvent("refresh", this);
53144         }else{
53145             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53146             var bt = this.getBodyTable();
53147             var tbody = bt.firstChild;
53148             var rows = bt.rows;
53149             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53150                 tbody.removeChild(rows[firstRow]);
53151             }
53152             this.stripeRows(firstRow);
53153             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53154         }
53155     },
53156
53157     updateRows : function(dataSource, firstRow, lastRow){
53158         var s = this.getScrollState();
53159         this.refresh();
53160         this.restoreScroll(s);
53161     },
53162
53163     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53164         if(!noRefresh){
53165            this.refresh();
53166         }
53167         this.updateHeaderSortState();
53168     },
53169
53170     getScrollState : function(){
53171         
53172         var sb = this.scroller.dom;
53173         return {left: sb.scrollLeft, top: sb.scrollTop};
53174     },
53175
53176     stripeRows : function(startRow){
53177         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53178             return;
53179         }
53180         startRow = startRow || 0;
53181         var rows = this.getBodyTable().rows;
53182         var lrows = this.getLockedTable().rows;
53183         var cls = ' x-grid-row-alt ';
53184         for(var i = startRow, len = rows.length; i < len; i++){
53185             var row = rows[i], lrow = lrows[i];
53186             var isAlt = ((i+1) % 2 == 0);
53187             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53188             if(isAlt == hasAlt){
53189                 continue;
53190             }
53191             if(isAlt){
53192                 row.className += " x-grid-row-alt";
53193             }else{
53194                 row.className = row.className.replace("x-grid-row-alt", "");
53195             }
53196             if(lrow){
53197                 lrow.className = row.className;
53198             }
53199         }
53200     },
53201
53202     restoreScroll : function(state){
53203         //Roo.log('GridView.restoreScroll');
53204         var sb = this.scroller.dom;
53205         sb.scrollLeft = state.left;
53206         sb.scrollTop = state.top;
53207         this.syncScroll();
53208     },
53209
53210     syncScroll : function(){
53211         //Roo.log('GridView.syncScroll');
53212         var sb = this.scroller.dom;
53213         var sh = this.mainHd.dom;
53214         var bs = this.mainBody.dom;
53215         var lv = this.lockedBody.dom;
53216         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53217         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53218     },
53219
53220     handleScroll : function(e){
53221         this.syncScroll();
53222         var sb = this.scroller.dom;
53223         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53224         e.stopEvent();
53225     },
53226
53227     handleWheel : function(e){
53228         var d = e.getWheelDelta();
53229         this.scroller.dom.scrollTop -= d*22;
53230         // set this here to prevent jumpy scrolling on large tables
53231         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53232         e.stopEvent();
53233     },
53234
53235     renderRows : function(startRow, endRow){
53236         // pull in all the crap needed to render rows
53237         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53238         var colCount = cm.getColumnCount();
53239
53240         if(ds.getCount() < 1){
53241             return ["", ""];
53242         }
53243
53244         // build a map for all the columns
53245         var cs = [];
53246         for(var i = 0; i < colCount; i++){
53247             var name = cm.getDataIndex(i);
53248             cs[i] = {
53249                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53250                 renderer : cm.getRenderer(i),
53251                 id : cm.getColumnId(i),
53252                 locked : cm.isLocked(i)
53253             };
53254         }
53255
53256         startRow = startRow || 0;
53257         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53258
53259         // records to render
53260         var rs = ds.getRange(startRow, endRow);
53261
53262         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53263     },
53264
53265     // As much as I hate to duplicate code, this was branched because FireFox really hates
53266     // [].join("") on strings. The performance difference was substantial enough to
53267     // branch this function
53268     doRender : Roo.isGecko ?
53269             function(cs, rs, ds, startRow, colCount, stripe){
53270                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53271                 // buffers
53272                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53273                 
53274                 var hasListener = this.grid.hasListener('rowclass');
53275                 var rowcfg = {};
53276                 for(var j = 0, len = rs.length; j < len; j++){
53277                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53278                     for(var i = 0; i < colCount; i++){
53279                         c = cs[i];
53280                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53281                         p.id = c.id;
53282                         p.css = p.attr = "";
53283                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53284                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53285                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53286                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53287                         }
53288                         var markup = ct.apply(p);
53289                         if(!c.locked){
53290                             cb+= markup;
53291                         }else{
53292                             lcb+= markup;
53293                         }
53294                     }
53295                     var alt = [];
53296                     if(stripe && ((rowIndex+1) % 2 == 0)){
53297                         alt.push("x-grid-row-alt")
53298                     }
53299                     if(r.dirty){
53300                         alt.push(  " x-grid-dirty-row");
53301                     }
53302                     rp.cells = lcb;
53303                     if(this.getRowClass){
53304                         alt.push(this.getRowClass(r, rowIndex));
53305                     }
53306                     if (hasListener) {
53307                         rowcfg = {
53308                              
53309                             record: r,
53310                             rowIndex : rowIndex,
53311                             rowClass : ''
53312                         }
53313                         this.grid.fireEvent('rowclass', this, rowcfg);
53314                         alt.push(rowcfg.rowClass);
53315                     }
53316                     rp.alt = alt.join(" ");
53317                     lbuf+= rt.apply(rp);
53318                     rp.cells = cb;
53319                     buf+=  rt.apply(rp);
53320                 }
53321                 return [lbuf, buf];
53322             } :
53323             function(cs, rs, ds, startRow, colCount, stripe){
53324                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53325                 // buffers
53326                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53327                 var hasListener = this.grid.hasListener('rowclass');
53328  
53329                 var rowcfg = {};
53330                 for(var j = 0, len = rs.length; j < len; j++){
53331                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53332                     for(var i = 0; i < colCount; i++){
53333                         c = cs[i];
53334                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53335                         p.id = c.id;
53336                         p.css = p.attr = "";
53337                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53338                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53339                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53340                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53341                         }
53342                         
53343                         var markup = ct.apply(p);
53344                         if(!c.locked){
53345                             cb[cb.length] = markup;
53346                         }else{
53347                             lcb[lcb.length] = markup;
53348                         }
53349                     }
53350                     var alt = [];
53351                     if(stripe && ((rowIndex+1) % 2 == 0)){
53352                         alt.push( "x-grid-row-alt");
53353                     }
53354                     if(r.dirty){
53355                         alt.push(" x-grid-dirty-row");
53356                     }
53357                     rp.cells = lcb;
53358                     if(this.getRowClass){
53359                         alt.push( this.getRowClass(r, rowIndex));
53360                     }
53361                     if (hasListener) {
53362                         rowcfg = {
53363                              
53364                             record: r,
53365                             rowIndex : rowIndex,
53366                             rowClass : ''
53367                         }
53368                         this.grid.fireEvent('rowclass', this, rowcfg);
53369                         alt.push(rowcfg.rowClass);
53370                     }
53371                     rp.alt = alt.join(" ");
53372                     rp.cells = lcb.join("");
53373                     lbuf[lbuf.length] = rt.apply(rp);
53374                     rp.cells = cb.join("");
53375                     buf[buf.length] =  rt.apply(rp);
53376                 }
53377                 return [lbuf.join(""), buf.join("")];
53378             },
53379
53380     renderBody : function(){
53381         var markup = this.renderRows();
53382         var bt = this.templates.body;
53383         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53384     },
53385
53386     /**
53387      * Refreshes the grid
53388      * @param {Boolean} headersToo
53389      */
53390     refresh : function(headersToo){
53391         this.fireEvent("beforerefresh", this);
53392         this.grid.stopEditing();
53393         var result = this.renderBody();
53394         this.lockedBody.update(result[0]);
53395         this.mainBody.update(result[1]);
53396         if(headersToo === true){
53397             this.updateHeaders();
53398             this.updateColumns();
53399             this.updateSplitters();
53400             this.updateHeaderSortState();
53401         }
53402         this.syncRowHeights();
53403         this.layout();
53404         this.fireEvent("refresh", this);
53405     },
53406
53407     handleColumnMove : function(cm, oldIndex, newIndex){
53408         this.indexMap = null;
53409         var s = this.getScrollState();
53410         this.refresh(true);
53411         this.restoreScroll(s);
53412         this.afterMove(newIndex);
53413     },
53414
53415     afterMove : function(colIndex){
53416         if(this.enableMoveAnim && Roo.enableFx){
53417             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53418         }
53419         // if multisort - fix sortOrder, and reload..
53420         if (this.grid.dataSource.multiSort) {
53421             // the we can call sort again..
53422             var dm = this.grid.dataSource;
53423             var cm = this.grid.colModel;
53424             var so = [];
53425             for(var i = 0; i < cm.config.length; i++ ) {
53426                 
53427                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53428                     continue; // dont' bother, it's not in sort list or being set.
53429                 }
53430                 
53431                 so.push(cm.config[i].dataIndex);
53432             };
53433             dm.sortOrder = so;
53434             dm.load(dm.lastOptions);
53435             
53436             
53437         }
53438         
53439     },
53440
53441     updateCell : function(dm, rowIndex, dataIndex){
53442         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53443         if(typeof colIndex == "undefined"){ // not present in grid
53444             return;
53445         }
53446         var cm = this.grid.colModel;
53447         var cell = this.getCell(rowIndex, colIndex);
53448         var cellText = this.getCellText(rowIndex, colIndex);
53449
53450         var p = {
53451             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53452             id : cm.getColumnId(colIndex),
53453             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53454         };
53455         var renderer = cm.getRenderer(colIndex);
53456         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53457         if(typeof val == "undefined" || val === "") val = "&#160;";
53458         cellText.innerHTML = val;
53459         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53460         this.syncRowHeights(rowIndex, rowIndex);
53461     },
53462
53463     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53464         var maxWidth = 0;
53465         if(this.grid.autoSizeHeaders){
53466             var h = this.getHeaderCellMeasure(colIndex);
53467             maxWidth = Math.max(maxWidth, h.scrollWidth);
53468         }
53469         var tb, index;
53470         if(this.cm.isLocked(colIndex)){
53471             tb = this.getLockedTable();
53472             index = colIndex;
53473         }else{
53474             tb = this.getBodyTable();
53475             index = colIndex - this.cm.getLockedCount();
53476         }
53477         if(tb && tb.rows){
53478             var rows = tb.rows;
53479             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53480             for(var i = 0; i < stopIndex; i++){
53481                 var cell = rows[i].childNodes[index].firstChild;
53482                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53483             }
53484         }
53485         return maxWidth + /*margin for error in IE*/ 5;
53486     },
53487     /**
53488      * Autofit a column to its content.
53489      * @param {Number} colIndex
53490      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53491      */
53492      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53493          if(this.cm.isHidden(colIndex)){
53494              return; // can't calc a hidden column
53495          }
53496         if(forceMinSize){
53497             var cid = this.cm.getColumnId(colIndex);
53498             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53499            if(this.grid.autoSizeHeaders){
53500                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53501            }
53502         }
53503         var newWidth = this.calcColumnWidth(colIndex);
53504         this.cm.setColumnWidth(colIndex,
53505             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53506         if(!suppressEvent){
53507             this.grid.fireEvent("columnresize", colIndex, newWidth);
53508         }
53509     },
53510
53511     /**
53512      * Autofits all columns to their content and then expands to fit any extra space in the grid
53513      */
53514      autoSizeColumns : function(){
53515         var cm = this.grid.colModel;
53516         var colCount = cm.getColumnCount();
53517         for(var i = 0; i < colCount; i++){
53518             this.autoSizeColumn(i, true, true);
53519         }
53520         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53521             this.fitColumns();
53522         }else{
53523             this.updateColumns();
53524             this.layout();
53525         }
53526     },
53527
53528     /**
53529      * Autofits all columns to the grid's width proportionate with their current size
53530      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53531      */
53532     fitColumns : function(reserveScrollSpace){
53533         var cm = this.grid.colModel;
53534         var colCount = cm.getColumnCount();
53535         var cols = [];
53536         var width = 0;
53537         var i, w;
53538         for (i = 0; i < colCount; i++){
53539             if(!cm.isHidden(i) && !cm.isFixed(i)){
53540                 w = cm.getColumnWidth(i);
53541                 cols.push(i);
53542                 cols.push(w);
53543                 width += w;
53544             }
53545         }
53546         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53547         if(reserveScrollSpace){
53548             avail -= 17;
53549         }
53550         var frac = (avail - cm.getTotalWidth())/width;
53551         while (cols.length){
53552             w = cols.pop();
53553             i = cols.pop();
53554             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53555         }
53556         this.updateColumns();
53557         this.layout();
53558     },
53559
53560     onRowSelect : function(rowIndex){
53561         var row = this.getRowComposite(rowIndex);
53562         row.addClass("x-grid-row-selected");
53563     },
53564
53565     onRowDeselect : function(rowIndex){
53566         var row = this.getRowComposite(rowIndex);
53567         row.removeClass("x-grid-row-selected");
53568     },
53569
53570     onCellSelect : function(row, col){
53571         var cell = this.getCell(row, col);
53572         if(cell){
53573             Roo.fly(cell).addClass("x-grid-cell-selected");
53574         }
53575     },
53576
53577     onCellDeselect : function(row, col){
53578         var cell = this.getCell(row, col);
53579         if(cell){
53580             Roo.fly(cell).removeClass("x-grid-cell-selected");
53581         }
53582     },
53583
53584     updateHeaderSortState : function(){
53585         
53586         // sort state can be single { field: xxx, direction : yyy}
53587         // or   { xxx=>ASC , yyy : DESC ..... }
53588         
53589         var mstate = {};
53590         if (!this.ds.multiSort) { 
53591             var state = this.ds.getSortState();
53592             if(!state){
53593                 return;
53594             }
53595             mstate[state.field] = state.direction;
53596             // FIXME... - this is not used here.. but might be elsewhere..
53597             this.sortState = state;
53598             
53599         } else {
53600             mstate = this.ds.sortToggle;
53601         }
53602         //remove existing sort classes..
53603         
53604         var sc = this.sortClasses;
53605         var hds = this.el.select(this.headerSelector).removeClass(sc);
53606         
53607         for(var f in mstate) {
53608         
53609             var sortColumn = this.cm.findColumnIndex(f);
53610             
53611             if(sortColumn != -1){
53612                 var sortDir = mstate[f];        
53613                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53614             }
53615         }
53616         
53617          
53618         
53619     },
53620
53621
53622     handleHeaderClick : function(g, index,e){
53623         
53624         Roo.log("header click");
53625         
53626         if (Roo.isTouch) {
53627             // touch events on header are handled by context
53628             this.handleHdCtx(g,index,e);
53629             return;
53630         }
53631         
53632         
53633         if(this.headersDisabled){
53634             return;
53635         }
53636         var dm = g.dataSource, cm = g.colModel;
53637         if(!cm.isSortable(index)){
53638             return;
53639         }
53640         g.stopEditing();
53641         
53642         if (dm.multiSort) {
53643             // update the sortOrder
53644             var so = [];
53645             for(var i = 0; i < cm.config.length; i++ ) {
53646                 
53647                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53648                     continue; // dont' bother, it's not in sort list or being set.
53649                 }
53650                 
53651                 so.push(cm.config[i].dataIndex);
53652             };
53653             dm.sortOrder = so;
53654         }
53655         
53656         
53657         dm.sort(cm.getDataIndex(index));
53658     },
53659
53660
53661     destroy : function(){
53662         if(this.colMenu){
53663             this.colMenu.removeAll();
53664             Roo.menu.MenuMgr.unregister(this.colMenu);
53665             this.colMenu.getEl().remove();
53666             delete this.colMenu;
53667         }
53668         if(this.hmenu){
53669             this.hmenu.removeAll();
53670             Roo.menu.MenuMgr.unregister(this.hmenu);
53671             this.hmenu.getEl().remove();
53672             delete this.hmenu;
53673         }
53674         if(this.grid.enableColumnMove){
53675             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53676             if(dds){
53677                 for(var dd in dds){
53678                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53679                         var elid = dds[dd].dragElId;
53680                         dds[dd].unreg();
53681                         Roo.get(elid).remove();
53682                     } else if(dds[dd].config.isTarget){
53683                         dds[dd].proxyTop.remove();
53684                         dds[dd].proxyBottom.remove();
53685                         dds[dd].unreg();
53686                     }
53687                     if(Roo.dd.DDM.locationCache[dd]){
53688                         delete Roo.dd.DDM.locationCache[dd];
53689                     }
53690                 }
53691                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53692             }
53693         }
53694         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53695         this.bind(null, null);
53696         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53697     },
53698
53699     handleLockChange : function(){
53700         this.refresh(true);
53701     },
53702
53703     onDenyColumnLock : function(){
53704
53705     },
53706
53707     onDenyColumnHide : function(){
53708
53709     },
53710
53711     handleHdMenuClick : function(item){
53712         var index = this.hdCtxIndex;
53713         var cm = this.cm, ds = this.ds;
53714         switch(item.id){
53715             case "asc":
53716                 ds.sort(cm.getDataIndex(index), "ASC");
53717                 break;
53718             case "desc":
53719                 ds.sort(cm.getDataIndex(index), "DESC");
53720                 break;
53721             case "lock":
53722                 var lc = cm.getLockedCount();
53723                 if(cm.getColumnCount(true) <= lc+1){
53724                     this.onDenyColumnLock();
53725                     return;
53726                 }
53727                 if(lc != index){
53728                     cm.setLocked(index, true, true);
53729                     cm.moveColumn(index, lc);
53730                     this.grid.fireEvent("columnmove", index, lc);
53731                 }else{
53732                     cm.setLocked(index, true);
53733                 }
53734             break;
53735             case "unlock":
53736                 var lc = cm.getLockedCount();
53737                 if((lc-1) != index){
53738                     cm.setLocked(index, false, true);
53739                     cm.moveColumn(index, lc-1);
53740                     this.grid.fireEvent("columnmove", index, lc-1);
53741                 }else{
53742                     cm.setLocked(index, false);
53743                 }
53744             break;
53745             case 'wider': // used to expand cols on touch..
53746             case 'narrow':
53747                 var cw = cm.getColumnWidth(index);
53748                 cw += (item.id == 'wider' ? 1 : -1) * 50;
53749                 cw = Math.max(0, cw);
53750                 cw = Math.min(cw,4000);
53751                 cm.setColumnWidth(index, cw);
53752                 break;
53753                 
53754             default:
53755                 index = cm.getIndexById(item.id.substr(4));
53756                 if(index != -1){
53757                     if(item.checked && cm.getColumnCount(true) <= 1){
53758                         this.onDenyColumnHide();
53759                         return false;
53760                     }
53761                     cm.setHidden(index, item.checked);
53762                 }
53763         }
53764         return true;
53765     },
53766
53767     beforeColMenuShow : function(){
53768         var cm = this.cm,  colCount = cm.getColumnCount();
53769         this.colMenu.removeAll();
53770         for(var i = 0; i < colCount; i++){
53771             this.colMenu.add(new Roo.menu.CheckItem({
53772                 id: "col-"+cm.getColumnId(i),
53773                 text: cm.getColumnHeader(i),
53774                 checked: !cm.isHidden(i),
53775                 hideOnClick:false
53776             }));
53777         }
53778     },
53779
53780     handleHdCtx : function(g, index, e){
53781         e.stopEvent();
53782         var hd = this.getHeaderCell(index);
53783         this.hdCtxIndex = index;
53784         var ms = this.hmenu.items, cm = this.cm;
53785         ms.get("asc").setDisabled(!cm.isSortable(index));
53786         ms.get("desc").setDisabled(!cm.isSortable(index));
53787         if(this.grid.enableColLock !== false){
53788             ms.get("lock").setDisabled(cm.isLocked(index));
53789             ms.get("unlock").setDisabled(!cm.isLocked(index));
53790         }
53791         this.hmenu.show(hd, "tl-bl");
53792     },
53793
53794     handleHdOver : function(e){
53795         var hd = this.findHeaderCell(e.getTarget());
53796         if(hd && !this.headersDisabled){
53797             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53798                this.fly(hd).addClass("x-grid-hd-over");
53799             }
53800         }
53801     },
53802
53803     handleHdOut : function(e){
53804         var hd = this.findHeaderCell(e.getTarget());
53805         if(hd){
53806             this.fly(hd).removeClass("x-grid-hd-over");
53807         }
53808     },
53809
53810     handleSplitDblClick : function(e, t){
53811         var i = this.getCellIndex(t);
53812         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53813             this.autoSizeColumn(i, true);
53814             this.layout();
53815         }
53816     },
53817
53818     render : function(){
53819
53820         var cm = this.cm;
53821         var colCount = cm.getColumnCount();
53822
53823         if(this.grid.monitorWindowResize === true){
53824             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53825         }
53826         var header = this.renderHeaders();
53827         var body = this.templates.body.apply({rows:""});
53828         var html = this.templates.master.apply({
53829             lockedBody: body,
53830             body: body,
53831             lockedHeader: header[0],
53832             header: header[1]
53833         });
53834
53835         //this.updateColumns();
53836
53837         this.grid.getGridEl().dom.innerHTML = html;
53838
53839         this.initElements();
53840         
53841         // a kludge to fix the random scolling effect in webkit
53842         this.el.on("scroll", function() {
53843             this.el.dom.scrollTop=0; // hopefully not recursive..
53844         },this);
53845
53846         this.scroller.on("scroll", this.handleScroll, this);
53847         this.lockedBody.on("mousewheel", this.handleWheel, this);
53848         this.mainBody.on("mousewheel", this.handleWheel, this);
53849
53850         this.mainHd.on("mouseover", this.handleHdOver, this);
53851         this.mainHd.on("mouseout", this.handleHdOut, this);
53852         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53853                 {delegate: "."+this.splitClass});
53854
53855         this.lockedHd.on("mouseover", this.handleHdOver, this);
53856         this.lockedHd.on("mouseout", this.handleHdOut, this);
53857         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53858                 {delegate: "."+this.splitClass});
53859
53860         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53861             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53862         }
53863
53864         this.updateSplitters();
53865
53866         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53867             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53868             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53869         }
53870
53871         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53872             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53873             this.hmenu.add(
53874                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53875                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53876             );
53877             if(this.grid.enableColLock !== false){
53878                 this.hmenu.add('-',
53879                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53880                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53881                 );
53882             }
53883             if (Roo.isTouch) {
53884                  this.hmenu.add('-',
53885                     {id:"wider", text: this.columnsWiderText},
53886                     {id:"narrow", text: this.columnsNarrowText }
53887                 );
53888                 
53889                  
53890             }
53891             
53892             if(this.grid.enableColumnHide !== false){
53893
53894                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53895                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53896                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53897
53898                 this.hmenu.add('-',
53899                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53900                 );
53901             }
53902             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53903
53904             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53905         }
53906
53907         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53908             this.dd = new Roo.grid.GridDragZone(this.grid, {
53909                 ddGroup : this.grid.ddGroup || 'GridDD'
53910             });
53911             
53912         }
53913
53914         /*
53915         for(var i = 0; i < colCount; i++){
53916             if(cm.isHidden(i)){
53917                 this.hideColumn(i);
53918             }
53919             if(cm.config[i].align){
53920                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53921                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53922             }
53923         }*/
53924         
53925         this.updateHeaderSortState();
53926
53927         this.beforeInitialResize();
53928         this.layout(true);
53929
53930         // two part rendering gives faster view to the user
53931         this.renderPhase2.defer(1, this);
53932     },
53933
53934     renderPhase2 : function(){
53935         // render the rows now
53936         this.refresh();
53937         if(this.grid.autoSizeColumns){
53938             this.autoSizeColumns();
53939         }
53940     },
53941
53942     beforeInitialResize : function(){
53943
53944     },
53945
53946     onColumnSplitterMoved : function(i, w){
53947         this.userResized = true;
53948         var cm = this.grid.colModel;
53949         cm.setColumnWidth(i, w, true);
53950         var cid = cm.getColumnId(i);
53951         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53952         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53953         this.updateSplitters();
53954         this.layout();
53955         this.grid.fireEvent("columnresize", i, w);
53956     },
53957
53958     syncRowHeights : function(startIndex, endIndex){
53959         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53960             startIndex = startIndex || 0;
53961             var mrows = this.getBodyTable().rows;
53962             var lrows = this.getLockedTable().rows;
53963             var len = mrows.length-1;
53964             endIndex = Math.min(endIndex || len, len);
53965             for(var i = startIndex; i <= endIndex; i++){
53966                 var m = mrows[i], l = lrows[i];
53967                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53968                 m.style.height = l.style.height = h + "px";
53969             }
53970         }
53971     },
53972
53973     layout : function(initialRender, is2ndPass){
53974         var g = this.grid;
53975         var auto = g.autoHeight;
53976         var scrollOffset = 16;
53977         var c = g.getGridEl(), cm = this.cm,
53978                 expandCol = g.autoExpandColumn,
53979                 gv = this;
53980         //c.beginMeasure();
53981
53982         if(!c.dom.offsetWidth){ // display:none?
53983             if(initialRender){
53984                 this.lockedWrap.show();
53985                 this.mainWrap.show();
53986             }
53987             return;
53988         }
53989
53990         var hasLock = this.cm.isLocked(0);
53991
53992         var tbh = this.headerPanel.getHeight();
53993         var bbh = this.footerPanel.getHeight();
53994
53995         if(auto){
53996             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53997             var newHeight = ch + c.getBorderWidth("tb");
53998             if(g.maxHeight){
53999                 newHeight = Math.min(g.maxHeight, newHeight);
54000             }
54001             c.setHeight(newHeight);
54002         }
54003
54004         if(g.autoWidth){
54005             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54006         }
54007
54008         var s = this.scroller;
54009
54010         var csize = c.getSize(true);
54011
54012         this.el.setSize(csize.width, csize.height);
54013
54014         this.headerPanel.setWidth(csize.width);
54015         this.footerPanel.setWidth(csize.width);
54016
54017         var hdHeight = this.mainHd.getHeight();
54018         var vw = csize.width;
54019         var vh = csize.height - (tbh + bbh);
54020
54021         s.setSize(vw, vh);
54022
54023         var bt = this.getBodyTable();
54024         var ltWidth = hasLock ?
54025                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54026
54027         var scrollHeight = bt.offsetHeight;
54028         var scrollWidth = ltWidth + bt.offsetWidth;
54029         var vscroll = false, hscroll = false;
54030
54031         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54032
54033         var lw = this.lockedWrap, mw = this.mainWrap;
54034         var lb = this.lockedBody, mb = this.mainBody;
54035
54036         setTimeout(function(){
54037             var t = s.dom.offsetTop;
54038             var w = s.dom.clientWidth,
54039                 h = s.dom.clientHeight;
54040
54041             lw.setTop(t);
54042             lw.setSize(ltWidth, h);
54043
54044             mw.setLeftTop(ltWidth, t);
54045             mw.setSize(w-ltWidth, h);
54046
54047             lb.setHeight(h-hdHeight);
54048             mb.setHeight(h-hdHeight);
54049
54050             if(is2ndPass !== true && !gv.userResized && expandCol){
54051                 // high speed resize without full column calculation
54052                 
54053                 var ci = cm.getIndexById(expandCol);
54054                 if (ci < 0) {
54055                     ci = cm.findColumnIndex(expandCol);
54056                 }
54057                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54058                 var expandId = cm.getColumnId(ci);
54059                 var  tw = cm.getTotalWidth(false);
54060                 var currentWidth = cm.getColumnWidth(ci);
54061                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54062                 if(currentWidth != cw){
54063                     cm.setColumnWidth(ci, cw, true);
54064                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54065                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54066                     gv.updateSplitters();
54067                     gv.layout(false, true);
54068                 }
54069             }
54070
54071             if(initialRender){
54072                 lw.show();
54073                 mw.show();
54074             }
54075             //c.endMeasure();
54076         }, 10);
54077     },
54078
54079     onWindowResize : function(){
54080         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54081             return;
54082         }
54083         this.layout();
54084     },
54085
54086     appendFooter : function(parentEl){
54087         return null;
54088     },
54089
54090     sortAscText : "Sort Ascending",
54091     sortDescText : "Sort Descending",
54092     lockText : "Lock Column",
54093     unlockText : "Unlock Column",
54094     columnsText : "Columns",
54095  
54096     columnsWiderText : "Wider",
54097     columnsNarrowText : "Thinner"
54098 });
54099
54100
54101 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54102     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54103     this.proxy.el.addClass('x-grid3-col-dd');
54104 };
54105
54106 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54107     handleMouseDown : function(e){
54108
54109     },
54110
54111     callHandleMouseDown : function(e){
54112         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54113     }
54114 });
54115 /*
54116  * Based on:
54117  * Ext JS Library 1.1.1
54118  * Copyright(c) 2006-2007, Ext JS, LLC.
54119  *
54120  * Originally Released Under LGPL - original licence link has changed is not relivant.
54121  *
54122  * Fork - LGPL
54123  * <script type="text/javascript">
54124  */
54125  
54126 // private
54127 // This is a support class used internally by the Grid components
54128 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54129     this.grid = grid;
54130     this.view = grid.getView();
54131     this.proxy = this.view.resizeProxy;
54132     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54133         "gridSplitters" + this.grid.getGridEl().id, {
54134         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54135     });
54136     this.setHandleElId(Roo.id(hd));
54137     this.setOuterHandleElId(Roo.id(hd2));
54138     this.scroll = false;
54139 };
54140 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54141     fly: Roo.Element.fly,
54142
54143     b4StartDrag : function(x, y){
54144         this.view.headersDisabled = true;
54145         this.proxy.setHeight(this.view.mainWrap.getHeight());
54146         var w = this.cm.getColumnWidth(this.cellIndex);
54147         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54148         this.resetConstraints();
54149         this.setXConstraint(minw, 1000);
54150         this.setYConstraint(0, 0);
54151         this.minX = x - minw;
54152         this.maxX = x + 1000;
54153         this.startPos = x;
54154         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54155     },
54156
54157
54158     handleMouseDown : function(e){
54159         ev = Roo.EventObject.setEvent(e);
54160         var t = this.fly(ev.getTarget());
54161         if(t.hasClass("x-grid-split")){
54162             this.cellIndex = this.view.getCellIndex(t.dom);
54163             this.split = t.dom;
54164             this.cm = this.grid.colModel;
54165             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54166                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54167             }
54168         }
54169     },
54170
54171     endDrag : function(e){
54172         this.view.headersDisabled = false;
54173         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54174         var diff = endX - this.startPos;
54175         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54176     },
54177
54178     autoOffset : function(){
54179         this.setDelta(0,0);
54180     }
54181 });/*
54182  * Based on:
54183  * Ext JS Library 1.1.1
54184  * Copyright(c) 2006-2007, Ext JS, LLC.
54185  *
54186  * Originally Released Under LGPL - original licence link has changed is not relivant.
54187  *
54188  * Fork - LGPL
54189  * <script type="text/javascript">
54190  */
54191  
54192 // private
54193 // This is a support class used internally by the Grid components
54194 Roo.grid.GridDragZone = function(grid, config){
54195     this.view = grid.getView();
54196     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54197     if(this.view.lockedBody){
54198         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54199         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54200     }
54201     this.scroll = false;
54202     this.grid = grid;
54203     this.ddel = document.createElement('div');
54204     this.ddel.className = 'x-grid-dd-wrap';
54205 };
54206
54207 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54208     ddGroup : "GridDD",
54209
54210     getDragData : function(e){
54211         var t = Roo.lib.Event.getTarget(e);
54212         var rowIndex = this.view.findRowIndex(t);
54213         var sm = this.grid.selModel;
54214             
54215         //Roo.log(rowIndex);
54216         
54217         if (sm.getSelectedCell) {
54218             // cell selection..
54219             if (!sm.getSelectedCell()) {
54220                 return false;
54221             }
54222             if (rowIndex != sm.getSelectedCell()[0]) {
54223                 return false;
54224             }
54225         
54226         }
54227         
54228         if(rowIndex !== false){
54229             
54230             // if editorgrid.. 
54231             
54232             
54233             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54234                
54235             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54236               //  
54237             //}
54238             if (e.hasModifier()){
54239                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54240             }
54241             
54242             Roo.log("getDragData");
54243             
54244             return {
54245                 grid: this.grid,
54246                 ddel: this.ddel,
54247                 rowIndex: rowIndex,
54248                 selections:sm.getSelections ? sm.getSelections() : (
54249                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54250                 )
54251             };
54252         }
54253         return false;
54254     },
54255
54256     onInitDrag : function(e){
54257         var data = this.dragData;
54258         this.ddel.innerHTML = this.grid.getDragDropText();
54259         this.proxy.update(this.ddel);
54260         // fire start drag?
54261     },
54262
54263     afterRepair : function(){
54264         this.dragging = false;
54265     },
54266
54267     getRepairXY : function(e, data){
54268         return false;
54269     },
54270
54271     onEndDrag : function(data, e){
54272         // fire end drag?
54273     },
54274
54275     onValidDrop : function(dd, e, id){
54276         // fire drag drop?
54277         this.hideProxy();
54278     },
54279
54280     beforeInvalidDrop : function(e, id){
54281
54282     }
54283 });/*
54284  * Based on:
54285  * Ext JS Library 1.1.1
54286  * Copyright(c) 2006-2007, Ext JS, LLC.
54287  *
54288  * Originally Released Under LGPL - original licence link has changed is not relivant.
54289  *
54290  * Fork - LGPL
54291  * <script type="text/javascript">
54292  */
54293  
54294
54295 /**
54296  * @class Roo.grid.ColumnModel
54297  * @extends Roo.util.Observable
54298  * This is the default implementation of a ColumnModel used by the Grid. It defines
54299  * the columns in the grid.
54300  * <br>Usage:<br>
54301  <pre><code>
54302  var colModel = new Roo.grid.ColumnModel([
54303         {header: "Ticker", width: 60, sortable: true, locked: true},
54304         {header: "Company Name", width: 150, sortable: true},
54305         {header: "Market Cap.", width: 100, sortable: true},
54306         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54307         {header: "Employees", width: 100, sortable: true, resizable: false}
54308  ]);
54309  </code></pre>
54310  * <p>
54311  
54312  * The config options listed for this class are options which may appear in each
54313  * individual column definition.
54314  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54315  * @constructor
54316  * @param {Object} config An Array of column config objects. See this class's
54317  * config objects for details.
54318 */
54319 Roo.grid.ColumnModel = function(config){
54320         /**
54321      * The config passed into the constructor
54322      */
54323     this.config = config;
54324     this.lookup = {};
54325
54326     // if no id, create one
54327     // if the column does not have a dataIndex mapping,
54328     // map it to the order it is in the config
54329     for(var i = 0, len = config.length; i < len; i++){
54330         var c = config[i];
54331         if(typeof c.dataIndex == "undefined"){
54332             c.dataIndex = i;
54333         }
54334         if(typeof c.renderer == "string"){
54335             c.renderer = Roo.util.Format[c.renderer];
54336         }
54337         if(typeof c.id == "undefined"){
54338             c.id = Roo.id();
54339         }
54340         if(c.editor && c.editor.xtype){
54341             c.editor  = Roo.factory(c.editor, Roo.grid);
54342         }
54343         if(c.editor && c.editor.isFormField){
54344             c.editor = new Roo.grid.GridEditor(c.editor);
54345         }
54346         this.lookup[c.id] = c;
54347     }
54348
54349     /**
54350      * The width of columns which have no width specified (defaults to 100)
54351      * @type Number
54352      */
54353     this.defaultWidth = 100;
54354
54355     /**
54356      * Default sortable of columns which have no sortable specified (defaults to false)
54357      * @type Boolean
54358      */
54359     this.defaultSortable = false;
54360
54361     this.addEvents({
54362         /**
54363              * @event widthchange
54364              * Fires when the width of a column changes.
54365              * @param {ColumnModel} this
54366              * @param {Number} columnIndex The column index
54367              * @param {Number} newWidth The new width
54368              */
54369             "widthchange": true,
54370         /**
54371              * @event headerchange
54372              * Fires when the text of a header changes.
54373              * @param {ColumnModel} this
54374              * @param {Number} columnIndex The column index
54375              * @param {Number} newText The new header text
54376              */
54377             "headerchange": true,
54378         /**
54379              * @event hiddenchange
54380              * Fires when a column is hidden or "unhidden".
54381              * @param {ColumnModel} this
54382              * @param {Number} columnIndex The column index
54383              * @param {Boolean} hidden true if hidden, false otherwise
54384              */
54385             "hiddenchange": true,
54386             /**
54387          * @event columnmoved
54388          * Fires when a column is moved.
54389          * @param {ColumnModel} this
54390          * @param {Number} oldIndex
54391          * @param {Number} newIndex
54392          */
54393         "columnmoved" : true,
54394         /**
54395          * @event columlockchange
54396          * Fires when a column's locked state is changed
54397          * @param {ColumnModel} this
54398          * @param {Number} colIndex
54399          * @param {Boolean} locked true if locked
54400          */
54401         "columnlockchange" : true
54402     });
54403     Roo.grid.ColumnModel.superclass.constructor.call(this);
54404 };
54405 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54406     /**
54407      * @cfg {String} header The header text to display in the Grid view.
54408      */
54409     /**
54410      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54411      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54412      * specified, the column's index is used as an index into the Record's data Array.
54413      */
54414     /**
54415      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54416      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54417      */
54418     /**
54419      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54420      * Defaults to the value of the {@link #defaultSortable} property.
54421      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54422      */
54423     /**
54424      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54425      */
54426     /**
54427      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54428      */
54429     /**
54430      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54431      */
54432     /**
54433      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54434      */
54435     /**
54436      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54437      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54438      * default renderer uses the raw data value.
54439      */
54440        /**
54441      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54442      */
54443     /**
54444      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54445      */
54446
54447     /**
54448      * Returns the id of the column at the specified index.
54449      * @param {Number} index The column index
54450      * @return {String} the id
54451      */
54452     getColumnId : function(index){
54453         return this.config[index].id;
54454     },
54455
54456     /**
54457      * Returns the column for a specified id.
54458      * @param {String} id The column id
54459      * @return {Object} the column
54460      */
54461     getColumnById : function(id){
54462         return this.lookup[id];
54463     },
54464
54465     
54466     /**
54467      * Returns the column for a specified dataIndex.
54468      * @param {String} dataIndex The column dataIndex
54469      * @return {Object|Boolean} the column or false if not found
54470      */
54471     getColumnByDataIndex: function(dataIndex){
54472         var index = this.findColumnIndex(dataIndex);
54473         return index > -1 ? this.config[index] : false;
54474     },
54475     
54476     /**
54477      * Returns the index for a specified column id.
54478      * @param {String} id The column id
54479      * @return {Number} the index, or -1 if not found
54480      */
54481     getIndexById : function(id){
54482         for(var i = 0, len = this.config.length; i < len; i++){
54483             if(this.config[i].id == id){
54484                 return i;
54485             }
54486         }
54487         return -1;
54488     },
54489     
54490     /**
54491      * Returns the index for a specified column dataIndex.
54492      * @param {String} dataIndex The column dataIndex
54493      * @return {Number} the index, or -1 if not found
54494      */
54495     
54496     findColumnIndex : function(dataIndex){
54497         for(var i = 0, len = this.config.length; i < len; i++){
54498             if(this.config[i].dataIndex == dataIndex){
54499                 return i;
54500             }
54501         }
54502         return -1;
54503     },
54504     
54505     
54506     moveColumn : function(oldIndex, newIndex){
54507         var c = this.config[oldIndex];
54508         this.config.splice(oldIndex, 1);
54509         this.config.splice(newIndex, 0, c);
54510         this.dataMap = null;
54511         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54512     },
54513
54514     isLocked : function(colIndex){
54515         return this.config[colIndex].locked === true;
54516     },
54517
54518     setLocked : function(colIndex, value, suppressEvent){
54519         if(this.isLocked(colIndex) == value){
54520             return;
54521         }
54522         this.config[colIndex].locked = value;
54523         if(!suppressEvent){
54524             this.fireEvent("columnlockchange", this, colIndex, value);
54525         }
54526     },
54527
54528     getTotalLockedWidth : function(){
54529         var totalWidth = 0;
54530         for(var i = 0; i < this.config.length; i++){
54531             if(this.isLocked(i) && !this.isHidden(i)){
54532                 this.totalWidth += this.getColumnWidth(i);
54533             }
54534         }
54535         return totalWidth;
54536     },
54537
54538     getLockedCount : function(){
54539         for(var i = 0, len = this.config.length; i < len; i++){
54540             if(!this.isLocked(i)){
54541                 return i;
54542             }
54543         }
54544     },
54545
54546     /**
54547      * Returns the number of columns.
54548      * @return {Number}
54549      */
54550     getColumnCount : function(visibleOnly){
54551         if(visibleOnly === true){
54552             var c = 0;
54553             for(var i = 0, len = this.config.length; i < len; i++){
54554                 if(!this.isHidden(i)){
54555                     c++;
54556                 }
54557             }
54558             return c;
54559         }
54560         return this.config.length;
54561     },
54562
54563     /**
54564      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54565      * @param {Function} fn
54566      * @param {Object} scope (optional)
54567      * @return {Array} result
54568      */
54569     getColumnsBy : function(fn, scope){
54570         var r = [];
54571         for(var i = 0, len = this.config.length; i < len; i++){
54572             var c = this.config[i];
54573             if(fn.call(scope||this, c, i) === true){
54574                 r[r.length] = c;
54575             }
54576         }
54577         return r;
54578     },
54579
54580     /**
54581      * Returns true if the specified column is sortable.
54582      * @param {Number} col The column index
54583      * @return {Boolean}
54584      */
54585     isSortable : function(col){
54586         if(typeof this.config[col].sortable == "undefined"){
54587             return this.defaultSortable;
54588         }
54589         return this.config[col].sortable;
54590     },
54591
54592     /**
54593      * Returns the rendering (formatting) function defined for the column.
54594      * @param {Number} col The column index.
54595      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54596      */
54597     getRenderer : function(col){
54598         if(!this.config[col].renderer){
54599             return Roo.grid.ColumnModel.defaultRenderer;
54600         }
54601         return this.config[col].renderer;
54602     },
54603
54604     /**
54605      * Sets the rendering (formatting) function for a column.
54606      * @param {Number} col The column index
54607      * @param {Function} fn The function to use to process the cell's raw data
54608      * to return HTML markup for the grid view. The render function is called with
54609      * the following parameters:<ul>
54610      * <li>Data value.</li>
54611      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54612      * <li>css A CSS style string to apply to the table cell.</li>
54613      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54614      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54615      * <li>Row index</li>
54616      * <li>Column index</li>
54617      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54618      */
54619     setRenderer : function(col, fn){
54620         this.config[col].renderer = fn;
54621     },
54622
54623     /**
54624      * Returns the width for the specified column.
54625      * @param {Number} col The column index
54626      * @return {Number}
54627      */
54628     getColumnWidth : function(col){
54629         return this.config[col].width * 1 || this.defaultWidth;
54630     },
54631
54632     /**
54633      * Sets the width for a column.
54634      * @param {Number} col The column index
54635      * @param {Number} width The new width
54636      */
54637     setColumnWidth : function(col, width, suppressEvent){
54638         this.config[col].width = width;
54639         this.totalWidth = null;
54640         if(!suppressEvent){
54641              this.fireEvent("widthchange", this, col, width);
54642         }
54643     },
54644
54645     /**
54646      * Returns the total width of all columns.
54647      * @param {Boolean} includeHidden True to include hidden column widths
54648      * @return {Number}
54649      */
54650     getTotalWidth : function(includeHidden){
54651         if(!this.totalWidth){
54652             this.totalWidth = 0;
54653             for(var i = 0, len = this.config.length; i < len; i++){
54654                 if(includeHidden || !this.isHidden(i)){
54655                     this.totalWidth += this.getColumnWidth(i);
54656                 }
54657             }
54658         }
54659         return this.totalWidth;
54660     },
54661
54662     /**
54663      * Returns the header for the specified column.
54664      * @param {Number} col The column index
54665      * @return {String}
54666      */
54667     getColumnHeader : function(col){
54668         return this.config[col].header;
54669     },
54670
54671     /**
54672      * Sets the header for a column.
54673      * @param {Number} col The column index
54674      * @param {String} header The new header
54675      */
54676     setColumnHeader : function(col, header){
54677         this.config[col].header = header;
54678         this.fireEvent("headerchange", this, col, header);
54679     },
54680
54681     /**
54682      * Returns the tooltip for the specified column.
54683      * @param {Number} col The column index
54684      * @return {String}
54685      */
54686     getColumnTooltip : function(col){
54687             return this.config[col].tooltip;
54688     },
54689     /**
54690      * Sets the tooltip for a column.
54691      * @param {Number} col The column index
54692      * @param {String} tooltip The new tooltip
54693      */
54694     setColumnTooltip : function(col, tooltip){
54695             this.config[col].tooltip = tooltip;
54696     },
54697
54698     /**
54699      * Returns the dataIndex for the specified column.
54700      * @param {Number} col The column index
54701      * @return {Number}
54702      */
54703     getDataIndex : function(col){
54704         return this.config[col].dataIndex;
54705     },
54706
54707     /**
54708      * Sets the dataIndex for a column.
54709      * @param {Number} col The column index
54710      * @param {Number} dataIndex The new dataIndex
54711      */
54712     setDataIndex : function(col, dataIndex){
54713         this.config[col].dataIndex = dataIndex;
54714     },
54715
54716     
54717     
54718     /**
54719      * Returns true if the cell is editable.
54720      * @param {Number} colIndex The column index
54721      * @param {Number} rowIndex The row index
54722      * @return {Boolean}
54723      */
54724     isCellEditable : function(colIndex, rowIndex){
54725         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54726     },
54727
54728     /**
54729      * Returns the editor defined for the cell/column.
54730      * return false or null to disable editing.
54731      * @param {Number} colIndex The column index
54732      * @param {Number} rowIndex The row index
54733      * @return {Object}
54734      */
54735     getCellEditor : function(colIndex, rowIndex){
54736         return this.config[colIndex].editor;
54737     },
54738
54739     /**
54740      * Sets if a column is editable.
54741      * @param {Number} col The column index
54742      * @param {Boolean} editable True if the column is editable
54743      */
54744     setEditable : function(col, editable){
54745         this.config[col].editable = editable;
54746     },
54747
54748
54749     /**
54750      * Returns true if the column is hidden.
54751      * @param {Number} colIndex The column index
54752      * @return {Boolean}
54753      */
54754     isHidden : function(colIndex){
54755         return this.config[colIndex].hidden;
54756     },
54757
54758
54759     /**
54760      * Returns true if the column width cannot be changed
54761      */
54762     isFixed : function(colIndex){
54763         return this.config[colIndex].fixed;
54764     },
54765
54766     /**
54767      * Returns true if the column can be resized
54768      * @return {Boolean}
54769      */
54770     isResizable : function(colIndex){
54771         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54772     },
54773     /**
54774      * Sets if a column is hidden.
54775      * @param {Number} colIndex The column index
54776      * @param {Boolean} hidden True if the column is hidden
54777      */
54778     setHidden : function(colIndex, hidden){
54779         this.config[colIndex].hidden = hidden;
54780         this.totalWidth = null;
54781         this.fireEvent("hiddenchange", this, colIndex, hidden);
54782     },
54783
54784     /**
54785      * Sets the editor for a column.
54786      * @param {Number} col The column index
54787      * @param {Object} editor The editor object
54788      */
54789     setEditor : function(col, editor){
54790         this.config[col].editor = editor;
54791     }
54792 });
54793
54794 Roo.grid.ColumnModel.defaultRenderer = function(value){
54795         if(typeof value == "string" && value.length < 1){
54796             return "&#160;";
54797         }
54798         return value;
54799 };
54800
54801 // Alias for backwards compatibility
54802 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54803 /*
54804  * Based on:
54805  * Ext JS Library 1.1.1
54806  * Copyright(c) 2006-2007, Ext JS, LLC.
54807  *
54808  * Originally Released Under LGPL - original licence link has changed is not relivant.
54809  *
54810  * Fork - LGPL
54811  * <script type="text/javascript">
54812  */
54813
54814 /**
54815  * @class Roo.grid.AbstractSelectionModel
54816  * @extends Roo.util.Observable
54817  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54818  * implemented by descendant classes.  This class should not be directly instantiated.
54819  * @constructor
54820  */
54821 Roo.grid.AbstractSelectionModel = function(){
54822     this.locked = false;
54823     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54824 };
54825
54826 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54827     /** @ignore Called by the grid automatically. Do not call directly. */
54828     init : function(grid){
54829         this.grid = grid;
54830         this.initEvents();
54831     },
54832
54833     /**
54834      * Locks the selections.
54835      */
54836     lock : function(){
54837         this.locked = true;
54838     },
54839
54840     /**
54841      * Unlocks the selections.
54842      */
54843     unlock : function(){
54844         this.locked = false;
54845     },
54846
54847     /**
54848      * Returns true if the selections are locked.
54849      * @return {Boolean}
54850      */
54851     isLocked : function(){
54852         return this.locked;
54853     }
54854 });/*
54855  * Based on:
54856  * Ext JS Library 1.1.1
54857  * Copyright(c) 2006-2007, Ext JS, LLC.
54858  *
54859  * Originally Released Under LGPL - original licence link has changed is not relivant.
54860  *
54861  * Fork - LGPL
54862  * <script type="text/javascript">
54863  */
54864 /**
54865  * @extends Roo.grid.AbstractSelectionModel
54866  * @class Roo.grid.RowSelectionModel
54867  * The default SelectionModel used by {@link Roo.grid.Grid}.
54868  * It supports multiple selections and keyboard selection/navigation. 
54869  * @constructor
54870  * @param {Object} config
54871  */
54872 Roo.grid.RowSelectionModel = function(config){
54873     Roo.apply(this, config);
54874     this.selections = new Roo.util.MixedCollection(false, function(o){
54875         return o.id;
54876     });
54877
54878     this.last = false;
54879     this.lastActive = false;
54880
54881     this.addEvents({
54882         /**
54883              * @event selectionchange
54884              * Fires when the selection changes
54885              * @param {SelectionModel} this
54886              */
54887             "selectionchange" : true,
54888         /**
54889              * @event afterselectionchange
54890              * Fires after the selection changes (eg. by key press or clicking)
54891              * @param {SelectionModel} this
54892              */
54893             "afterselectionchange" : true,
54894         /**
54895              * @event beforerowselect
54896              * Fires when a row is selected being selected, return false to cancel.
54897              * @param {SelectionModel} this
54898              * @param {Number} rowIndex The selected index
54899              * @param {Boolean} keepExisting False if other selections will be cleared
54900              */
54901             "beforerowselect" : true,
54902         /**
54903              * @event rowselect
54904              * Fires when a row is selected.
54905              * @param {SelectionModel} this
54906              * @param {Number} rowIndex The selected index
54907              * @param {Roo.data.Record} r The record
54908              */
54909             "rowselect" : true,
54910         /**
54911              * @event rowdeselect
54912              * Fires when a row is deselected.
54913              * @param {SelectionModel} this
54914              * @param {Number} rowIndex The selected index
54915              */
54916         "rowdeselect" : true
54917     });
54918     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54919     this.locked = false;
54920 };
54921
54922 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54923     /**
54924      * @cfg {Boolean} singleSelect
54925      * True to allow selection of only one row at a time (defaults to false)
54926      */
54927     singleSelect : false,
54928
54929     // private
54930     initEvents : function(){
54931
54932         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54933             this.grid.on("mousedown", this.handleMouseDown, this);
54934         }else{ // allow click to work like normal
54935             this.grid.on("rowclick", this.handleDragableRowClick, this);
54936         }
54937
54938         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54939             "up" : function(e){
54940                 if(!e.shiftKey){
54941                     this.selectPrevious(e.shiftKey);
54942                 }else if(this.last !== false && this.lastActive !== false){
54943                     var last = this.last;
54944                     this.selectRange(this.last,  this.lastActive-1);
54945                     this.grid.getView().focusRow(this.lastActive);
54946                     if(last !== false){
54947                         this.last = last;
54948                     }
54949                 }else{
54950                     this.selectFirstRow();
54951                 }
54952                 this.fireEvent("afterselectionchange", this);
54953             },
54954             "down" : function(e){
54955                 if(!e.shiftKey){
54956                     this.selectNext(e.shiftKey);
54957                 }else if(this.last !== false && this.lastActive !== false){
54958                     var last = this.last;
54959                     this.selectRange(this.last,  this.lastActive+1);
54960                     this.grid.getView().focusRow(this.lastActive);
54961                     if(last !== false){
54962                         this.last = last;
54963                     }
54964                 }else{
54965                     this.selectFirstRow();
54966                 }
54967                 this.fireEvent("afterselectionchange", this);
54968             },
54969             scope: this
54970         });
54971
54972         var view = this.grid.view;
54973         view.on("refresh", this.onRefresh, this);
54974         view.on("rowupdated", this.onRowUpdated, this);
54975         view.on("rowremoved", this.onRemove, this);
54976     },
54977
54978     // private
54979     onRefresh : function(){
54980         var ds = this.grid.dataSource, i, v = this.grid.view;
54981         var s = this.selections;
54982         s.each(function(r){
54983             if((i = ds.indexOfId(r.id)) != -1){
54984                 v.onRowSelect(i);
54985             }else{
54986                 s.remove(r);
54987             }
54988         });
54989     },
54990
54991     // private
54992     onRemove : function(v, index, r){
54993         this.selections.remove(r);
54994     },
54995
54996     // private
54997     onRowUpdated : function(v, index, r){
54998         if(this.isSelected(r)){
54999             v.onRowSelect(index);
55000         }
55001     },
55002
55003     /**
55004      * Select records.
55005      * @param {Array} records The records to select
55006      * @param {Boolean} keepExisting (optional) True to keep existing selections
55007      */
55008     selectRecords : function(records, keepExisting){
55009         if(!keepExisting){
55010             this.clearSelections();
55011         }
55012         var ds = this.grid.dataSource;
55013         for(var i = 0, len = records.length; i < len; i++){
55014             this.selectRow(ds.indexOf(records[i]), true);
55015         }
55016     },
55017
55018     /**
55019      * Gets the number of selected rows.
55020      * @return {Number}
55021      */
55022     getCount : function(){
55023         return this.selections.length;
55024     },
55025
55026     /**
55027      * Selects the first row in the grid.
55028      */
55029     selectFirstRow : function(){
55030         this.selectRow(0);
55031     },
55032
55033     /**
55034      * Select the last row.
55035      * @param {Boolean} keepExisting (optional) True to keep existing selections
55036      */
55037     selectLastRow : function(keepExisting){
55038         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55039     },
55040
55041     /**
55042      * Selects the row immediately following the last selected row.
55043      * @param {Boolean} keepExisting (optional) True to keep existing selections
55044      */
55045     selectNext : function(keepExisting){
55046         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55047             this.selectRow(this.last+1, keepExisting);
55048             this.grid.getView().focusRow(this.last);
55049         }
55050     },
55051
55052     /**
55053      * Selects the row that precedes the last selected row.
55054      * @param {Boolean} keepExisting (optional) True to keep existing selections
55055      */
55056     selectPrevious : function(keepExisting){
55057         if(this.last){
55058             this.selectRow(this.last-1, keepExisting);
55059             this.grid.getView().focusRow(this.last);
55060         }
55061     },
55062
55063     /**
55064      * Returns the selected records
55065      * @return {Array} Array of selected records
55066      */
55067     getSelections : function(){
55068         return [].concat(this.selections.items);
55069     },
55070
55071     /**
55072      * Returns the first selected record.
55073      * @return {Record}
55074      */
55075     getSelected : function(){
55076         return this.selections.itemAt(0);
55077     },
55078
55079
55080     /**
55081      * Clears all selections.
55082      */
55083     clearSelections : function(fast){
55084         if(this.locked) return;
55085         if(fast !== true){
55086             var ds = this.grid.dataSource;
55087             var s = this.selections;
55088             s.each(function(r){
55089                 this.deselectRow(ds.indexOfId(r.id));
55090             }, this);
55091             s.clear();
55092         }else{
55093             this.selections.clear();
55094         }
55095         this.last = false;
55096     },
55097
55098
55099     /**
55100      * Selects all rows.
55101      */
55102     selectAll : function(){
55103         if(this.locked) return;
55104         this.selections.clear();
55105         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55106             this.selectRow(i, true);
55107         }
55108     },
55109
55110     /**
55111      * Returns True if there is a selection.
55112      * @return {Boolean}
55113      */
55114     hasSelection : function(){
55115         return this.selections.length > 0;
55116     },
55117
55118     /**
55119      * Returns True if the specified row is selected.
55120      * @param {Number/Record} record The record or index of the record to check
55121      * @return {Boolean}
55122      */
55123     isSelected : function(index){
55124         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55125         return (r && this.selections.key(r.id) ? true : false);
55126     },
55127
55128     /**
55129      * Returns True if the specified record id is selected.
55130      * @param {String} id The id of record to check
55131      * @return {Boolean}
55132      */
55133     isIdSelected : function(id){
55134         return (this.selections.key(id) ? true : false);
55135     },
55136
55137     // private
55138     handleMouseDown : function(e, t){
55139         var view = this.grid.getView(), rowIndex;
55140         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55141             return;
55142         };
55143         if(e.shiftKey && this.last !== false){
55144             var last = this.last;
55145             this.selectRange(last, rowIndex, e.ctrlKey);
55146             this.last = last; // reset the last
55147             view.focusRow(rowIndex);
55148         }else{
55149             var isSelected = this.isSelected(rowIndex);
55150             if(e.button !== 0 && isSelected){
55151                 view.focusRow(rowIndex);
55152             }else if(e.ctrlKey && isSelected){
55153                 this.deselectRow(rowIndex);
55154             }else if(!isSelected){
55155                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55156                 view.focusRow(rowIndex);
55157             }
55158         }
55159         this.fireEvent("afterselectionchange", this);
55160     },
55161     // private
55162     handleDragableRowClick :  function(grid, rowIndex, e) 
55163     {
55164         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55165             this.selectRow(rowIndex, false);
55166             grid.view.focusRow(rowIndex);
55167              this.fireEvent("afterselectionchange", this);
55168         }
55169     },
55170     
55171     /**
55172      * Selects multiple rows.
55173      * @param {Array} rows Array of the indexes of the row to select
55174      * @param {Boolean} keepExisting (optional) True to keep existing selections
55175      */
55176     selectRows : function(rows, keepExisting){
55177         if(!keepExisting){
55178             this.clearSelections();
55179         }
55180         for(var i = 0, len = rows.length; i < len; i++){
55181             this.selectRow(rows[i], true);
55182         }
55183     },
55184
55185     /**
55186      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55187      * @param {Number} startRow The index of the first row in the range
55188      * @param {Number} endRow The index of the last row in the range
55189      * @param {Boolean} keepExisting (optional) True to retain existing selections
55190      */
55191     selectRange : function(startRow, endRow, keepExisting){
55192         if(this.locked) return;
55193         if(!keepExisting){
55194             this.clearSelections();
55195         }
55196         if(startRow <= endRow){
55197             for(var i = startRow; i <= endRow; i++){
55198                 this.selectRow(i, true);
55199             }
55200         }else{
55201             for(var i = startRow; i >= endRow; i--){
55202                 this.selectRow(i, true);
55203             }
55204         }
55205     },
55206
55207     /**
55208      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55209      * @param {Number} startRow The index of the first row in the range
55210      * @param {Number} endRow The index of the last row in the range
55211      */
55212     deselectRange : function(startRow, endRow, preventViewNotify){
55213         if(this.locked) return;
55214         for(var i = startRow; i <= endRow; i++){
55215             this.deselectRow(i, preventViewNotify);
55216         }
55217     },
55218
55219     /**
55220      * Selects a row.
55221      * @param {Number} row The index of the row to select
55222      * @param {Boolean} keepExisting (optional) True to keep existing selections
55223      */
55224     selectRow : function(index, keepExisting, preventViewNotify){
55225         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55226         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55227             if(!keepExisting || this.singleSelect){
55228                 this.clearSelections();
55229             }
55230             var r = this.grid.dataSource.getAt(index);
55231             this.selections.add(r);
55232             this.last = this.lastActive = index;
55233             if(!preventViewNotify){
55234                 this.grid.getView().onRowSelect(index);
55235             }
55236             this.fireEvent("rowselect", this, index, r);
55237             this.fireEvent("selectionchange", this);
55238         }
55239     },
55240
55241     /**
55242      * Deselects a row.
55243      * @param {Number} row The index of the row to deselect
55244      */
55245     deselectRow : function(index, preventViewNotify){
55246         if(this.locked) return;
55247         if(this.last == index){
55248             this.last = false;
55249         }
55250         if(this.lastActive == index){
55251             this.lastActive = false;
55252         }
55253         var r = this.grid.dataSource.getAt(index);
55254         this.selections.remove(r);
55255         if(!preventViewNotify){
55256             this.grid.getView().onRowDeselect(index);
55257         }
55258         this.fireEvent("rowdeselect", this, index);
55259         this.fireEvent("selectionchange", this);
55260     },
55261
55262     // private
55263     restoreLast : function(){
55264         if(this._last){
55265             this.last = this._last;
55266         }
55267     },
55268
55269     // private
55270     acceptsNav : function(row, col, cm){
55271         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55272     },
55273
55274     // private
55275     onEditorKey : function(field, e){
55276         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55277         if(k == e.TAB){
55278             e.stopEvent();
55279             ed.completeEdit();
55280             if(e.shiftKey){
55281                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55282             }else{
55283                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55284             }
55285         }else if(k == e.ENTER && !e.ctrlKey){
55286             e.stopEvent();
55287             ed.completeEdit();
55288             if(e.shiftKey){
55289                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55290             }else{
55291                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55292             }
55293         }else if(k == e.ESC){
55294             ed.cancelEdit();
55295         }
55296         if(newCell){
55297             g.startEditing(newCell[0], newCell[1]);
55298         }
55299     }
55300 });/*
55301  * Based on:
55302  * Ext JS Library 1.1.1
55303  * Copyright(c) 2006-2007, Ext JS, LLC.
55304  *
55305  * Originally Released Under LGPL - original licence link has changed is not relivant.
55306  *
55307  * Fork - LGPL
55308  * <script type="text/javascript">
55309  */
55310 /**
55311  * @class Roo.grid.CellSelectionModel
55312  * @extends Roo.grid.AbstractSelectionModel
55313  * This class provides the basic implementation for cell selection in a grid.
55314  * @constructor
55315  * @param {Object} config The object containing the configuration of this model.
55316  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55317  */
55318 Roo.grid.CellSelectionModel = function(config){
55319     Roo.apply(this, config);
55320
55321     this.selection = null;
55322
55323     this.addEvents({
55324         /**
55325              * @event beforerowselect
55326              * Fires before a cell is selected.
55327              * @param {SelectionModel} this
55328              * @param {Number} rowIndex The selected row index
55329              * @param {Number} colIndex The selected cell index
55330              */
55331             "beforecellselect" : true,
55332         /**
55333              * @event cellselect
55334              * Fires when a cell is selected.
55335              * @param {SelectionModel} this
55336              * @param {Number} rowIndex The selected row index
55337              * @param {Number} colIndex The selected cell index
55338              */
55339             "cellselect" : true,
55340         /**
55341              * @event selectionchange
55342              * Fires when the active selection changes.
55343              * @param {SelectionModel} this
55344              * @param {Object} selection null for no selection or an object (o) with two properties
55345                 <ul>
55346                 <li>o.record: the record object for the row the selection is in</li>
55347                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55348                 </ul>
55349              */
55350             "selectionchange" : true,
55351         /**
55352              * @event tabend
55353              * Fires when the tab (or enter) was pressed on the last editable cell
55354              * You can use this to trigger add new row.
55355              * @param {SelectionModel} this
55356              */
55357             "tabend" : true,
55358          /**
55359              * @event beforeeditnext
55360              * Fires before the next editable sell is made active
55361              * You can use this to skip to another cell or fire the tabend
55362              *    if you set cell to false
55363              * @param {Object} eventdata object : { cell : [ row, col ] } 
55364              */
55365             "beforeeditnext" : true
55366     });
55367     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55368 };
55369
55370 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55371     
55372     enter_is_tab: false,
55373
55374     /** @ignore */
55375     initEvents : function(){
55376         this.grid.on("mousedown", this.handleMouseDown, this);
55377         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55378         var view = this.grid.view;
55379         view.on("refresh", this.onViewChange, this);
55380         view.on("rowupdated", this.onRowUpdated, this);
55381         view.on("beforerowremoved", this.clearSelections, this);
55382         view.on("beforerowsinserted", this.clearSelections, this);
55383         if(this.grid.isEditor){
55384             this.grid.on("beforeedit", this.beforeEdit,  this);
55385         }
55386     },
55387
55388         //private
55389     beforeEdit : function(e){
55390         this.select(e.row, e.column, false, true, e.record);
55391     },
55392
55393         //private
55394     onRowUpdated : function(v, index, r){
55395         if(this.selection && this.selection.record == r){
55396             v.onCellSelect(index, this.selection.cell[1]);
55397         }
55398     },
55399
55400         //private
55401     onViewChange : function(){
55402         this.clearSelections(true);
55403     },
55404
55405         /**
55406          * Returns the currently selected cell,.
55407          * @return {Array} The selected cell (row, column) or null if none selected.
55408          */
55409     getSelectedCell : function(){
55410         return this.selection ? this.selection.cell : null;
55411     },
55412
55413     /**
55414      * Clears all selections.
55415      * @param {Boolean} true to prevent the gridview from being notified about the change.
55416      */
55417     clearSelections : function(preventNotify){
55418         var s = this.selection;
55419         if(s){
55420             if(preventNotify !== true){
55421                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55422             }
55423             this.selection = null;
55424             this.fireEvent("selectionchange", this, null);
55425         }
55426     },
55427
55428     /**
55429      * Returns true if there is a selection.
55430      * @return {Boolean}
55431      */
55432     hasSelection : function(){
55433         return this.selection ? true : false;
55434     },
55435
55436     /** @ignore */
55437     handleMouseDown : function(e, t){
55438         var v = this.grid.getView();
55439         if(this.isLocked()){
55440             return;
55441         };
55442         var row = v.findRowIndex(t);
55443         var cell = v.findCellIndex(t);
55444         if(row !== false && cell !== false){
55445             this.select(row, cell);
55446         }
55447     },
55448
55449     /**
55450      * Selects a cell.
55451      * @param {Number} rowIndex
55452      * @param {Number} collIndex
55453      */
55454     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55455         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55456             this.clearSelections();
55457             r = r || this.grid.dataSource.getAt(rowIndex);
55458             this.selection = {
55459                 record : r,
55460                 cell : [rowIndex, colIndex]
55461             };
55462             if(!preventViewNotify){
55463                 var v = this.grid.getView();
55464                 v.onCellSelect(rowIndex, colIndex);
55465                 if(preventFocus !== true){
55466                     v.focusCell(rowIndex, colIndex);
55467                 }
55468             }
55469             this.fireEvent("cellselect", this, rowIndex, colIndex);
55470             this.fireEvent("selectionchange", this, this.selection);
55471         }
55472     },
55473
55474         //private
55475     isSelectable : function(rowIndex, colIndex, cm){
55476         return !cm.isHidden(colIndex);
55477     },
55478
55479     /** @ignore */
55480     handleKeyDown : function(e){
55481         //Roo.log('Cell Sel Model handleKeyDown');
55482         if(!e.isNavKeyPress()){
55483             return;
55484         }
55485         var g = this.grid, s = this.selection;
55486         if(!s){
55487             e.stopEvent();
55488             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55489             if(cell){
55490                 this.select(cell[0], cell[1]);
55491             }
55492             return;
55493         }
55494         var sm = this;
55495         var walk = function(row, col, step){
55496             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55497         };
55498         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55499         var newCell;
55500
55501       
55502
55503         switch(k){
55504             case e.TAB:
55505                 // handled by onEditorKey
55506                 if (g.isEditor && g.editing) {
55507                     return;
55508                 }
55509                 if(e.shiftKey) {
55510                     newCell = walk(r, c-1, -1);
55511                 } else {
55512                     newCell = walk(r, c+1, 1);
55513                 }
55514                 break;
55515             
55516             case e.DOWN:
55517                newCell = walk(r+1, c, 1);
55518                 break;
55519             
55520             case e.UP:
55521                 newCell = walk(r-1, c, -1);
55522                 break;
55523             
55524             case e.RIGHT:
55525                 newCell = walk(r, c+1, 1);
55526                 break;
55527             
55528             case e.LEFT:
55529                 newCell = walk(r, c-1, -1);
55530                 break;
55531             
55532             case e.ENTER:
55533                 
55534                 if(g.isEditor && !g.editing){
55535                    g.startEditing(r, c);
55536                    e.stopEvent();
55537                    return;
55538                 }
55539                 
55540                 
55541              break;
55542         };
55543         if(newCell){
55544             this.select(newCell[0], newCell[1]);
55545             e.stopEvent();
55546             
55547         }
55548     },
55549
55550     acceptsNav : function(row, col, cm){
55551         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55552     },
55553     /**
55554      * Selects a cell.
55555      * @param {Number} field (not used) - as it's normally used as a listener
55556      * @param {Number} e - event - fake it by using
55557      *
55558      * var e = Roo.EventObjectImpl.prototype;
55559      * e.keyCode = e.TAB
55560      *
55561      * 
55562      */
55563     onEditorKey : function(field, e){
55564         
55565         var k = e.getKey(),
55566             newCell,
55567             g = this.grid,
55568             ed = g.activeEditor,
55569             forward = false;
55570         ///Roo.log('onEditorKey' + k);
55571         
55572         
55573         if (this.enter_is_tab && k == e.ENTER) {
55574             k = e.TAB;
55575         }
55576         
55577         if(k == e.TAB){
55578             if(e.shiftKey){
55579                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55580             }else{
55581                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55582                 forward = true;
55583             }
55584             
55585             e.stopEvent();
55586             
55587         } else if(k == e.ENTER &&  !e.ctrlKey){
55588             ed.completeEdit();
55589             e.stopEvent();
55590             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55591         
55592                 } else if(k == e.ESC){
55593             ed.cancelEdit();
55594         }
55595                 
55596         if (newCell) {
55597             var ecall = { cell : newCell, forward : forward };
55598             this.fireEvent('beforeeditnext', ecall );
55599             newCell = ecall.cell;
55600                         forward = ecall.forward;
55601         }
55602                 
55603         if(newCell){
55604             //Roo.log('next cell after edit');
55605             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55606         } else if (forward) {
55607             // tabbed past last
55608             this.fireEvent.defer(100, this, ['tabend',this]);
55609         }
55610     }
55611 });/*
55612  * Based on:
55613  * Ext JS Library 1.1.1
55614  * Copyright(c) 2006-2007, Ext JS, LLC.
55615  *
55616  * Originally Released Under LGPL - original licence link has changed is not relivant.
55617  *
55618  * Fork - LGPL
55619  * <script type="text/javascript">
55620  */
55621  
55622 /**
55623  * @class Roo.grid.EditorGrid
55624  * @extends Roo.grid.Grid
55625  * Class for creating and editable grid.
55626  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55627  * The container MUST have some type of size defined for the grid to fill. The container will be 
55628  * automatically set to position relative if it isn't already.
55629  * @param {Object} dataSource The data model to bind to
55630  * @param {Object} colModel The column model with info about this grid's columns
55631  */
55632 Roo.grid.EditorGrid = function(container, config){
55633     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55634     this.getGridEl().addClass("xedit-grid");
55635
55636     if(!this.selModel){
55637         this.selModel = new Roo.grid.CellSelectionModel();
55638     }
55639
55640     this.activeEditor = null;
55641
55642         this.addEvents({
55643             /**
55644              * @event beforeedit
55645              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55646              * <ul style="padding:5px;padding-left:16px;">
55647              * <li>grid - This grid</li>
55648              * <li>record - The record being edited</li>
55649              * <li>field - The field name being edited</li>
55650              * <li>value - The value for the field being edited.</li>
55651              * <li>row - The grid row index</li>
55652              * <li>column - The grid column index</li>
55653              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55654              * </ul>
55655              * @param {Object} e An edit event (see above for description)
55656              */
55657             "beforeedit" : true,
55658             /**
55659              * @event afteredit
55660              * Fires after a cell is edited. <br />
55661              * <ul style="padding:5px;padding-left:16px;">
55662              * <li>grid - This grid</li>
55663              * <li>record - The record being edited</li>
55664              * <li>field - The field name being edited</li>
55665              * <li>value - The value being set</li>
55666              * <li>originalValue - The original value for the field, before the edit.</li>
55667              * <li>row - The grid row index</li>
55668              * <li>column - The grid column index</li>
55669              * </ul>
55670              * @param {Object} e An edit event (see above for description)
55671              */
55672             "afteredit" : true,
55673             /**
55674              * @event validateedit
55675              * Fires after a cell is edited, but before the value is set in the record. 
55676          * You can use this to modify the value being set in the field, Return false
55677              * to cancel the change. The edit event object has the following properties <br />
55678              * <ul style="padding:5px;padding-left:16px;">
55679          * <li>editor - This editor</li>
55680              * <li>grid - This grid</li>
55681              * <li>record - The record being edited</li>
55682              * <li>field - The field name being edited</li>
55683              * <li>value - The value being set</li>
55684              * <li>originalValue - The original value for the field, before the edit.</li>
55685              * <li>row - The grid row index</li>
55686              * <li>column - The grid column index</li>
55687              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55688              * </ul>
55689              * @param {Object} e An edit event (see above for description)
55690              */
55691             "validateedit" : true
55692         });
55693     this.on("bodyscroll", this.stopEditing,  this);
55694     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55695 };
55696
55697 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55698     /**
55699      * @cfg {Number} clicksToEdit
55700      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55701      */
55702     clicksToEdit: 2,
55703
55704     // private
55705     isEditor : true,
55706     // private
55707     trackMouseOver: false, // causes very odd FF errors
55708
55709     onCellDblClick : function(g, row, col){
55710         this.startEditing(row, col);
55711     },
55712
55713     onEditComplete : function(ed, value, startValue){
55714         this.editing = false;
55715         this.activeEditor = null;
55716         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55717         var r = ed.record;
55718         var field = this.colModel.getDataIndex(ed.col);
55719         var e = {
55720             grid: this,
55721             record: r,
55722             field: field,
55723             originalValue: startValue,
55724             value: value,
55725             row: ed.row,
55726             column: ed.col,
55727             cancel:false,
55728             editor: ed
55729         };
55730         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55731         cell.show();
55732           
55733         if(String(value) !== String(startValue)){
55734             
55735             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55736                 r.set(field, e.value);
55737                 // if we are dealing with a combo box..
55738                 // then we also set the 'name' colum to be the displayField
55739                 if (ed.field.displayField && ed.field.name) {
55740                     r.set(ed.field.name, ed.field.el.dom.value);
55741                 }
55742                 
55743                 delete e.cancel; //?? why!!!
55744                 this.fireEvent("afteredit", e);
55745             }
55746         } else {
55747             this.fireEvent("afteredit", e); // always fire it!
55748         }
55749         this.view.focusCell(ed.row, ed.col);
55750     },
55751
55752     /**
55753      * Starts editing the specified for the specified row/column
55754      * @param {Number} rowIndex
55755      * @param {Number} colIndex
55756      */
55757     startEditing : function(row, col){
55758         this.stopEditing();
55759         if(this.colModel.isCellEditable(col, row)){
55760             this.view.ensureVisible(row, col, true);
55761           
55762             var r = this.dataSource.getAt(row);
55763             var field = this.colModel.getDataIndex(col);
55764             var cell = Roo.get(this.view.getCell(row,col));
55765             var e = {
55766                 grid: this,
55767                 record: r,
55768                 field: field,
55769                 value: r.data[field],
55770                 row: row,
55771                 column: col,
55772                 cancel:false 
55773             };
55774             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55775                 this.editing = true;
55776                 var ed = this.colModel.getCellEditor(col, row);
55777                 
55778                 if (!ed) {
55779                     return;
55780                 }
55781                 if(!ed.rendered){
55782                     ed.render(ed.parentEl || document.body);
55783                 }
55784                 ed.field.reset();
55785                
55786                 cell.hide();
55787                 
55788                 (function(){ // complex but required for focus issues in safari, ie and opera
55789                     ed.row = row;
55790                     ed.col = col;
55791                     ed.record = r;
55792                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55793                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55794                     this.activeEditor = ed;
55795                     var v = r.data[field];
55796                     ed.startEdit(this.view.getCell(row, col), v);
55797                     // combo's with 'displayField and name set
55798                     if (ed.field.displayField && ed.field.name) {
55799                         ed.field.el.dom.value = r.data[ed.field.name];
55800                     }
55801                     
55802                     
55803                 }).defer(50, this);
55804             }
55805         }
55806     },
55807         
55808     /**
55809      * Stops any active editing
55810      */
55811     stopEditing : function(){
55812         if(this.activeEditor){
55813             this.activeEditor.completeEdit();
55814         }
55815         this.activeEditor = null;
55816     },
55817         
55818          /**
55819      * Called to get grid's drag proxy text, by default returns this.ddText.
55820      * @return {String}
55821      */
55822     getDragDropText : function(){
55823         var count = this.selModel.getSelectedCell() ? 1 : 0;
55824         return String.format(this.ddText, count, count == 1 ? '' : 's');
55825     }
55826         
55827 });/*
55828  * Based on:
55829  * Ext JS Library 1.1.1
55830  * Copyright(c) 2006-2007, Ext JS, LLC.
55831  *
55832  * Originally Released Under LGPL - original licence link has changed is not relivant.
55833  *
55834  * Fork - LGPL
55835  * <script type="text/javascript">
55836  */
55837
55838 // private - not really -- you end up using it !
55839 // This is a support class used internally by the Grid components
55840
55841 /**
55842  * @class Roo.grid.GridEditor
55843  * @extends Roo.Editor
55844  * Class for creating and editable grid elements.
55845  * @param {Object} config any settings (must include field)
55846  */
55847 Roo.grid.GridEditor = function(field, config){
55848     if (!config && field.field) {
55849         config = field;
55850         field = Roo.factory(config.field, Roo.form);
55851     }
55852     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55853     field.monitorTab = false;
55854 };
55855
55856 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55857     
55858     /**
55859      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55860      */
55861     
55862     alignment: "tl-tl",
55863     autoSize: "width",
55864     hideEl : false,
55865     cls: "x-small-editor x-grid-editor",
55866     shim:false,
55867     shadow:"frame"
55868 });/*
55869  * Based on:
55870  * Ext JS Library 1.1.1
55871  * Copyright(c) 2006-2007, Ext JS, LLC.
55872  *
55873  * Originally Released Under LGPL - original licence link has changed is not relivant.
55874  *
55875  * Fork - LGPL
55876  * <script type="text/javascript">
55877  */
55878   
55879
55880   
55881 Roo.grid.PropertyRecord = Roo.data.Record.create([
55882     {name:'name',type:'string'},  'value'
55883 ]);
55884
55885
55886 Roo.grid.PropertyStore = function(grid, source){
55887     this.grid = grid;
55888     this.store = new Roo.data.Store({
55889         recordType : Roo.grid.PropertyRecord
55890     });
55891     this.store.on('update', this.onUpdate,  this);
55892     if(source){
55893         this.setSource(source);
55894     }
55895     Roo.grid.PropertyStore.superclass.constructor.call(this);
55896 };
55897
55898
55899
55900 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55901     setSource : function(o){
55902         this.source = o;
55903         this.store.removeAll();
55904         var data = [];
55905         for(var k in o){
55906             if(this.isEditableValue(o[k])){
55907                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55908             }
55909         }
55910         this.store.loadRecords({records: data}, {}, true);
55911     },
55912
55913     onUpdate : function(ds, record, type){
55914         if(type == Roo.data.Record.EDIT){
55915             var v = record.data['value'];
55916             var oldValue = record.modified['value'];
55917             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55918                 this.source[record.id] = v;
55919                 record.commit();
55920                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55921             }else{
55922                 record.reject();
55923             }
55924         }
55925     },
55926
55927     getProperty : function(row){
55928        return this.store.getAt(row);
55929     },
55930
55931     isEditableValue: function(val){
55932         if(val && val instanceof Date){
55933             return true;
55934         }else if(typeof val == 'object' || typeof val == 'function'){
55935             return false;
55936         }
55937         return true;
55938     },
55939
55940     setValue : function(prop, value){
55941         this.source[prop] = value;
55942         this.store.getById(prop).set('value', value);
55943     },
55944
55945     getSource : function(){
55946         return this.source;
55947     }
55948 });
55949
55950 Roo.grid.PropertyColumnModel = function(grid, store){
55951     this.grid = grid;
55952     var g = Roo.grid;
55953     g.PropertyColumnModel.superclass.constructor.call(this, [
55954         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55955         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55956     ]);
55957     this.store = store;
55958     this.bselect = Roo.DomHelper.append(document.body, {
55959         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55960             {tag: 'option', value: 'true', html: 'true'},
55961             {tag: 'option', value: 'false', html: 'false'}
55962         ]
55963     });
55964     Roo.id(this.bselect);
55965     var f = Roo.form;
55966     this.editors = {
55967         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55968         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55969         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55970         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55971         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55972     };
55973     this.renderCellDelegate = this.renderCell.createDelegate(this);
55974     this.renderPropDelegate = this.renderProp.createDelegate(this);
55975 };
55976
55977 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55978     
55979     
55980     nameText : 'Name',
55981     valueText : 'Value',
55982     
55983     dateFormat : 'm/j/Y',
55984     
55985     
55986     renderDate : function(dateVal){
55987         return dateVal.dateFormat(this.dateFormat);
55988     },
55989
55990     renderBool : function(bVal){
55991         return bVal ? 'true' : 'false';
55992     },
55993
55994     isCellEditable : function(colIndex, rowIndex){
55995         return colIndex == 1;
55996     },
55997
55998     getRenderer : function(col){
55999         return col == 1 ?
56000             this.renderCellDelegate : this.renderPropDelegate;
56001     },
56002
56003     renderProp : function(v){
56004         return this.getPropertyName(v);
56005     },
56006
56007     renderCell : function(val){
56008         var rv = val;
56009         if(val instanceof Date){
56010             rv = this.renderDate(val);
56011         }else if(typeof val == 'boolean'){
56012             rv = this.renderBool(val);
56013         }
56014         return Roo.util.Format.htmlEncode(rv);
56015     },
56016
56017     getPropertyName : function(name){
56018         var pn = this.grid.propertyNames;
56019         return pn && pn[name] ? pn[name] : name;
56020     },
56021
56022     getCellEditor : function(colIndex, rowIndex){
56023         var p = this.store.getProperty(rowIndex);
56024         var n = p.data['name'], val = p.data['value'];
56025         
56026         if(typeof(this.grid.customEditors[n]) == 'string'){
56027             return this.editors[this.grid.customEditors[n]];
56028         }
56029         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56030             return this.grid.customEditors[n];
56031         }
56032         if(val instanceof Date){
56033             return this.editors['date'];
56034         }else if(typeof val == 'number'){
56035             return this.editors['number'];
56036         }else if(typeof val == 'boolean'){
56037             return this.editors['boolean'];
56038         }else{
56039             return this.editors['string'];
56040         }
56041     }
56042 });
56043
56044 /**
56045  * @class Roo.grid.PropertyGrid
56046  * @extends Roo.grid.EditorGrid
56047  * This class represents the  interface of a component based property grid control.
56048  * <br><br>Usage:<pre><code>
56049  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56050       
56051  });
56052  // set any options
56053  grid.render();
56054  * </code></pre>
56055   
56056  * @constructor
56057  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56058  * The container MUST have some type of size defined for the grid to fill. The container will be
56059  * automatically set to position relative if it isn't already.
56060  * @param {Object} config A config object that sets properties on this grid.
56061  */
56062 Roo.grid.PropertyGrid = function(container, config){
56063     config = config || {};
56064     var store = new Roo.grid.PropertyStore(this);
56065     this.store = store;
56066     var cm = new Roo.grid.PropertyColumnModel(this, store);
56067     store.store.sort('name', 'ASC');
56068     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56069         ds: store.store,
56070         cm: cm,
56071         enableColLock:false,
56072         enableColumnMove:false,
56073         stripeRows:false,
56074         trackMouseOver: false,
56075         clicksToEdit:1
56076     }, config));
56077     this.getGridEl().addClass('x-props-grid');
56078     this.lastEditRow = null;
56079     this.on('columnresize', this.onColumnResize, this);
56080     this.addEvents({
56081          /**
56082              * @event beforepropertychange
56083              * Fires before a property changes (return false to stop?)
56084              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56085              * @param {String} id Record Id
56086              * @param {String} newval New Value
56087          * @param {String} oldval Old Value
56088              */
56089         "beforepropertychange": true,
56090         /**
56091              * @event propertychange
56092              * Fires after a property changes
56093              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56094              * @param {String} id Record Id
56095              * @param {String} newval New Value
56096          * @param {String} oldval Old Value
56097              */
56098         "propertychange": true
56099     });
56100     this.customEditors = this.customEditors || {};
56101 };
56102 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56103     
56104      /**
56105      * @cfg {Object} customEditors map of colnames=> custom editors.
56106      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56107      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56108      * false disables editing of the field.
56109          */
56110     
56111       /**
56112      * @cfg {Object} propertyNames map of property Names to their displayed value
56113          */
56114     
56115     render : function(){
56116         Roo.grid.PropertyGrid.superclass.render.call(this);
56117         this.autoSize.defer(100, this);
56118     },
56119
56120     autoSize : function(){
56121         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56122         if(this.view){
56123             this.view.fitColumns();
56124         }
56125     },
56126
56127     onColumnResize : function(){
56128         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56129         this.autoSize();
56130     },
56131     /**
56132      * Sets the data for the Grid
56133      * accepts a Key => Value object of all the elements avaiable.
56134      * @param {Object} data  to appear in grid.
56135      */
56136     setSource : function(source){
56137         this.store.setSource(source);
56138         //this.autoSize();
56139     },
56140     /**
56141      * Gets all the data from the grid.
56142      * @return {Object} data  data stored in grid
56143      */
56144     getSource : function(){
56145         return this.store.getSource();
56146     }
56147 });/*
56148   
56149  * Licence LGPL
56150  
56151  */
56152  
56153 /**
56154  * @class Roo.grid.Calendar
56155  * @extends Roo.util.Grid
56156  * This class extends the Grid to provide a calendar widget
56157  * <br><br>Usage:<pre><code>
56158  var grid = new Roo.grid.Calendar("my-container-id", {
56159      ds: myDataStore,
56160      cm: myColModel,
56161      selModel: mySelectionModel,
56162      autoSizeColumns: true,
56163      monitorWindowResize: false,
56164      trackMouseOver: true
56165      eventstore : real data store..
56166  });
56167  // set any options
56168  grid.render();
56169   
56170   * @constructor
56171  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56172  * The container MUST have some type of size defined for the grid to fill. The container will be
56173  * automatically set to position relative if it isn't already.
56174  * @param {Object} config A config object that sets properties on this grid.
56175  */
56176 Roo.grid.Calendar = function(container, config){
56177         // initialize the container
56178         this.container = Roo.get(container);
56179         this.container.update("");
56180         this.container.setStyle("overflow", "hidden");
56181     this.container.addClass('x-grid-container');
56182
56183     this.id = this.container.id;
56184
56185     Roo.apply(this, config);
56186     // check and correct shorthanded configs
56187     
56188     var rows = [];
56189     var d =1;
56190     for (var r = 0;r < 6;r++) {
56191         
56192         rows[r]=[];
56193         for (var c =0;c < 7;c++) {
56194             rows[r][c]= '';
56195         }
56196     }
56197     if (this.eventStore) {
56198         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56199         this.eventStore.on('load',this.onLoad, this);
56200         this.eventStore.on('beforeload',this.clearEvents, this);
56201          
56202     }
56203     
56204     this.dataSource = new Roo.data.Store({
56205             proxy: new Roo.data.MemoryProxy(rows),
56206             reader: new Roo.data.ArrayReader({}, [
56207                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56208     });
56209
56210     this.dataSource.load();
56211     this.ds = this.dataSource;
56212     this.ds.xmodule = this.xmodule || false;
56213     
56214     
56215     var cellRender = function(v,x,r)
56216     {
56217         return String.format(
56218             '<div class="fc-day  fc-widget-content"><div>' +
56219                 '<div class="fc-event-container"></div>' +
56220                 '<div class="fc-day-number">{0}</div>'+
56221                 
56222                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56223             '</div></div>', v);
56224     
56225     }
56226     
56227     
56228     this.colModel = new Roo.grid.ColumnModel( [
56229         {
56230             xtype: 'ColumnModel',
56231             xns: Roo.grid,
56232             dataIndex : 'weekday0',
56233             header : 'Sunday',
56234             renderer : cellRender
56235         },
56236         {
56237             xtype: 'ColumnModel',
56238             xns: Roo.grid,
56239             dataIndex : 'weekday1',
56240             header : 'Monday',
56241             renderer : cellRender
56242         },
56243         {
56244             xtype: 'ColumnModel',
56245             xns: Roo.grid,
56246             dataIndex : 'weekday2',
56247             header : 'Tuesday',
56248             renderer : cellRender
56249         },
56250         {
56251             xtype: 'ColumnModel',
56252             xns: Roo.grid,
56253             dataIndex : 'weekday3',
56254             header : 'Wednesday',
56255             renderer : cellRender
56256         },
56257         {
56258             xtype: 'ColumnModel',
56259             xns: Roo.grid,
56260             dataIndex : 'weekday4',
56261             header : 'Thursday',
56262             renderer : cellRender
56263         },
56264         {
56265             xtype: 'ColumnModel',
56266             xns: Roo.grid,
56267             dataIndex : 'weekday5',
56268             header : 'Friday',
56269             renderer : cellRender
56270         },
56271         {
56272             xtype: 'ColumnModel',
56273             xns: Roo.grid,
56274             dataIndex : 'weekday6',
56275             header : 'Saturday',
56276             renderer : cellRender
56277         }
56278     ]);
56279     this.cm = this.colModel;
56280     this.cm.xmodule = this.xmodule || false;
56281  
56282         
56283           
56284     //this.selModel = new Roo.grid.CellSelectionModel();
56285     //this.sm = this.selModel;
56286     //this.selModel.init(this);
56287     
56288     
56289     if(this.width){
56290         this.container.setWidth(this.width);
56291     }
56292
56293     if(this.height){
56294         this.container.setHeight(this.height);
56295     }
56296     /** @private */
56297         this.addEvents({
56298         // raw events
56299         /**
56300          * @event click
56301          * The raw click event for the entire grid.
56302          * @param {Roo.EventObject} e
56303          */
56304         "click" : true,
56305         /**
56306          * @event dblclick
56307          * The raw dblclick event for the entire grid.
56308          * @param {Roo.EventObject} e
56309          */
56310         "dblclick" : true,
56311         /**
56312          * @event contextmenu
56313          * The raw contextmenu event for the entire grid.
56314          * @param {Roo.EventObject} e
56315          */
56316         "contextmenu" : true,
56317         /**
56318          * @event mousedown
56319          * The raw mousedown event for the entire grid.
56320          * @param {Roo.EventObject} e
56321          */
56322         "mousedown" : true,
56323         /**
56324          * @event mouseup
56325          * The raw mouseup event for the entire grid.
56326          * @param {Roo.EventObject} e
56327          */
56328         "mouseup" : true,
56329         /**
56330          * @event mouseover
56331          * The raw mouseover event for the entire grid.
56332          * @param {Roo.EventObject} e
56333          */
56334         "mouseover" : true,
56335         /**
56336          * @event mouseout
56337          * The raw mouseout event for the entire grid.
56338          * @param {Roo.EventObject} e
56339          */
56340         "mouseout" : true,
56341         /**
56342          * @event keypress
56343          * The raw keypress event for the entire grid.
56344          * @param {Roo.EventObject} e
56345          */
56346         "keypress" : true,
56347         /**
56348          * @event keydown
56349          * The raw keydown event for the entire grid.
56350          * @param {Roo.EventObject} e
56351          */
56352         "keydown" : true,
56353
56354         // custom events
56355
56356         /**
56357          * @event cellclick
56358          * Fires when a cell is clicked
56359          * @param {Grid} this
56360          * @param {Number} rowIndex
56361          * @param {Number} columnIndex
56362          * @param {Roo.EventObject} e
56363          */
56364         "cellclick" : true,
56365         /**
56366          * @event celldblclick
56367          * Fires when a cell is double clicked
56368          * @param {Grid} this
56369          * @param {Number} rowIndex
56370          * @param {Number} columnIndex
56371          * @param {Roo.EventObject} e
56372          */
56373         "celldblclick" : true,
56374         /**
56375          * @event rowclick
56376          * Fires when a row is clicked
56377          * @param {Grid} this
56378          * @param {Number} rowIndex
56379          * @param {Roo.EventObject} e
56380          */
56381         "rowclick" : true,
56382         /**
56383          * @event rowdblclick
56384          * Fires when a row is double clicked
56385          * @param {Grid} this
56386          * @param {Number} rowIndex
56387          * @param {Roo.EventObject} e
56388          */
56389         "rowdblclick" : true,
56390         /**
56391          * @event headerclick
56392          * Fires when a header is clicked
56393          * @param {Grid} this
56394          * @param {Number} columnIndex
56395          * @param {Roo.EventObject} e
56396          */
56397         "headerclick" : true,
56398         /**
56399          * @event headerdblclick
56400          * Fires when a header cell is double clicked
56401          * @param {Grid} this
56402          * @param {Number} columnIndex
56403          * @param {Roo.EventObject} e
56404          */
56405         "headerdblclick" : true,
56406         /**
56407          * @event rowcontextmenu
56408          * Fires when a row is right clicked
56409          * @param {Grid} this
56410          * @param {Number} rowIndex
56411          * @param {Roo.EventObject} e
56412          */
56413         "rowcontextmenu" : true,
56414         /**
56415          * @event cellcontextmenu
56416          * Fires when a cell is right clicked
56417          * @param {Grid} this
56418          * @param {Number} rowIndex
56419          * @param {Number} cellIndex
56420          * @param {Roo.EventObject} e
56421          */
56422          "cellcontextmenu" : true,
56423         /**
56424          * @event headercontextmenu
56425          * Fires when a header is right clicked
56426          * @param {Grid} this
56427          * @param {Number} columnIndex
56428          * @param {Roo.EventObject} e
56429          */
56430         "headercontextmenu" : true,
56431         /**
56432          * @event bodyscroll
56433          * Fires when the body element is scrolled
56434          * @param {Number} scrollLeft
56435          * @param {Number} scrollTop
56436          */
56437         "bodyscroll" : true,
56438         /**
56439          * @event columnresize
56440          * Fires when the user resizes a column
56441          * @param {Number} columnIndex
56442          * @param {Number} newSize
56443          */
56444         "columnresize" : true,
56445         /**
56446          * @event columnmove
56447          * Fires when the user moves a column
56448          * @param {Number} oldIndex
56449          * @param {Number} newIndex
56450          */
56451         "columnmove" : true,
56452         /**
56453          * @event startdrag
56454          * Fires when row(s) start being dragged
56455          * @param {Grid} this
56456          * @param {Roo.GridDD} dd The drag drop object
56457          * @param {event} e The raw browser event
56458          */
56459         "startdrag" : true,
56460         /**
56461          * @event enddrag
56462          * Fires when a drag operation is complete
56463          * @param {Grid} this
56464          * @param {Roo.GridDD} dd The drag drop object
56465          * @param {event} e The raw browser event
56466          */
56467         "enddrag" : true,
56468         /**
56469          * @event dragdrop
56470          * Fires when dragged row(s) are dropped on a valid DD target
56471          * @param {Grid} this
56472          * @param {Roo.GridDD} dd The drag drop object
56473          * @param {String} targetId The target drag drop object
56474          * @param {event} e The raw browser event
56475          */
56476         "dragdrop" : true,
56477         /**
56478          * @event dragover
56479          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56480          * @param {Grid} this
56481          * @param {Roo.GridDD} dd The drag drop object
56482          * @param {String} targetId The target drag drop object
56483          * @param {event} e The raw browser event
56484          */
56485         "dragover" : true,
56486         /**
56487          * @event dragenter
56488          *  Fires when the dragged row(s) first cross another DD target while being dragged
56489          * @param {Grid} this
56490          * @param {Roo.GridDD} dd The drag drop object
56491          * @param {String} targetId The target drag drop object
56492          * @param {event} e The raw browser event
56493          */
56494         "dragenter" : true,
56495         /**
56496          * @event dragout
56497          * Fires when the dragged row(s) leave another DD target while being dragged
56498          * @param {Grid} this
56499          * @param {Roo.GridDD} dd The drag drop object
56500          * @param {String} targetId The target drag drop object
56501          * @param {event} e The raw browser event
56502          */
56503         "dragout" : true,
56504         /**
56505          * @event rowclass
56506          * Fires when a row is rendered, so you can change add a style to it.
56507          * @param {GridView} gridview   The grid view
56508          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56509          */
56510         'rowclass' : true,
56511
56512         /**
56513          * @event render
56514          * Fires when the grid is rendered
56515          * @param {Grid} grid
56516          */
56517         'render' : true,
56518             /**
56519              * @event select
56520              * Fires when a date is selected
56521              * @param {DatePicker} this
56522              * @param {Date} date The selected date
56523              */
56524         'select': true,
56525         /**
56526              * @event monthchange
56527              * Fires when the displayed month changes 
56528              * @param {DatePicker} this
56529              * @param {Date} date The selected month
56530              */
56531         'monthchange': true,
56532         /**
56533              * @event evententer
56534              * Fires when mouse over an event
56535              * @param {Calendar} this
56536              * @param {event} Event
56537              */
56538         'evententer': true,
56539         /**
56540              * @event eventleave
56541              * Fires when the mouse leaves an
56542              * @param {Calendar} this
56543              * @param {event}
56544              */
56545         'eventleave': true,
56546         /**
56547              * @event eventclick
56548              * Fires when the mouse click an
56549              * @param {Calendar} this
56550              * @param {event}
56551              */
56552         'eventclick': true,
56553         /**
56554              * @event eventrender
56555              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56556              * @param {Calendar} this
56557              * @param {data} data to be modified
56558              */
56559         'eventrender': true
56560         
56561     });
56562
56563     Roo.grid.Grid.superclass.constructor.call(this);
56564     this.on('render', function() {
56565         this.view.el.addClass('x-grid-cal'); 
56566         
56567         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56568
56569     },this);
56570     
56571     if (!Roo.grid.Calendar.style) {
56572         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56573             
56574             
56575             '.x-grid-cal .x-grid-col' :  {
56576                 height: 'auto !important',
56577                 'vertical-align': 'top'
56578             },
56579             '.x-grid-cal  .fc-event-hori' : {
56580                 height: '14px'
56581             }
56582              
56583             
56584         }, Roo.id());
56585     }
56586
56587     
56588     
56589 };
56590 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56591     /**
56592      * @cfg {Store} eventStore The store that loads events.
56593      */
56594     eventStore : 25,
56595
56596      
56597     activeDate : false,
56598     startDay : 0,
56599     autoWidth : true,
56600     monitorWindowResize : false,
56601
56602     
56603     resizeColumns : function() {
56604         var col = (this.view.el.getWidth() / 7) - 3;
56605         // loop through cols, and setWidth
56606         for(var i =0 ; i < 7 ; i++){
56607             this.cm.setColumnWidth(i, col);
56608         }
56609     },
56610      setDate :function(date) {
56611         
56612         Roo.log('setDate?');
56613         
56614         this.resizeColumns();
56615         var vd = this.activeDate;
56616         this.activeDate = date;
56617 //        if(vd && this.el){
56618 //            var t = date.getTime();
56619 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56620 //                Roo.log('using add remove');
56621 //                
56622 //                this.fireEvent('monthchange', this, date);
56623 //                
56624 //                this.cells.removeClass("fc-state-highlight");
56625 //                this.cells.each(function(c){
56626 //                   if(c.dateValue == t){
56627 //                       c.addClass("fc-state-highlight");
56628 //                       setTimeout(function(){
56629 //                            try{c.dom.firstChild.focus();}catch(e){}
56630 //                       }, 50);
56631 //                       return false;
56632 //                   }
56633 //                   return true;
56634 //                });
56635 //                return;
56636 //            }
56637 //        }
56638         
56639         var days = date.getDaysInMonth();
56640         
56641         var firstOfMonth = date.getFirstDateOfMonth();
56642         var startingPos = firstOfMonth.getDay()-this.startDay;
56643         
56644         if(startingPos < this.startDay){
56645             startingPos += 7;
56646         }
56647         
56648         var pm = date.add(Date.MONTH, -1);
56649         var prevStart = pm.getDaysInMonth()-startingPos;
56650 //        
56651         
56652         
56653         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56654         
56655         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
56656         //this.cells.addClassOnOver('fc-state-hover');
56657         
56658         var cells = this.cells.elements;
56659         var textEls = this.textNodes;
56660         
56661         //Roo.each(cells, function(cell){
56662         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
56663         //});
56664         
56665         days += startingPos;
56666
56667         // convert everything to numbers so it's fast
56668         var day = 86400000;
56669         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
56670         //Roo.log(d);
56671         //Roo.log(pm);
56672         //Roo.log(prevStart);
56673         
56674         var today = new Date().clearTime().getTime();
56675         var sel = date.clearTime().getTime();
56676         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
56677         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
56678         var ddMatch = this.disabledDatesRE;
56679         var ddText = this.disabledDatesText;
56680         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
56681         var ddaysText = this.disabledDaysText;
56682         var format = this.format;
56683         
56684         var setCellClass = function(cal, cell){
56685             
56686             //Roo.log('set Cell Class');
56687             cell.title = "";
56688             var t = d.getTime();
56689             
56690             //Roo.log(d);
56691             
56692             
56693             cell.dateValue = t;
56694             if(t == today){
56695                 cell.className += " fc-today";
56696                 cell.className += " fc-state-highlight";
56697                 cell.title = cal.todayText;
56698             }
56699             if(t == sel){
56700                 // disable highlight in other month..
56701                 cell.className += " fc-state-highlight";
56702                 
56703             }
56704             // disabling
56705             if(t < min) {
56706                 //cell.className = " fc-state-disabled";
56707                 cell.title = cal.minText;
56708                 return;
56709             }
56710             if(t > max) {
56711                 //cell.className = " fc-state-disabled";
56712                 cell.title = cal.maxText;
56713                 return;
56714             }
56715             if(ddays){
56716                 if(ddays.indexOf(d.getDay()) != -1){
56717                     // cell.title = ddaysText;
56718                    // cell.className = " fc-state-disabled";
56719                 }
56720             }
56721             if(ddMatch && format){
56722                 var fvalue = d.dateFormat(format);
56723                 if(ddMatch.test(fvalue)){
56724                     cell.title = ddText.replace("%0", fvalue);
56725                    cell.className = " fc-state-disabled";
56726                 }
56727             }
56728             
56729             if (!cell.initialClassName) {
56730                 cell.initialClassName = cell.dom.className;
56731             }
56732             
56733             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
56734         };
56735
56736         var i = 0;
56737         
56738         for(; i < startingPos; i++) {
56739             cells[i].dayName =  (++prevStart);
56740             Roo.log(textEls[i]);
56741             d.setDate(d.getDate()+1);
56742             
56743             //cells[i].className = "fc-past fc-other-month";
56744             setCellClass(this, cells[i]);
56745         }
56746         
56747         var intDay = 0;
56748         
56749         for(; i < days; i++){
56750             intDay = i - startingPos + 1;
56751             cells[i].dayName =  (intDay);
56752             d.setDate(d.getDate()+1);
56753             
56754             cells[i].className = ''; // "x-date-active";
56755             setCellClass(this, cells[i]);
56756         }
56757         var extraDays = 0;
56758         
56759         for(; i < 42; i++) {
56760             //textEls[i].innerHTML = (++extraDays);
56761             
56762             d.setDate(d.getDate()+1);
56763             cells[i].dayName = (++extraDays);
56764             cells[i].className = "fc-future fc-other-month";
56765             setCellClass(this, cells[i]);
56766         }
56767         
56768         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
56769         
56770         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
56771         
56772         // this will cause all the cells to mis
56773         var rows= [];
56774         var i =0;
56775         for (var r = 0;r < 6;r++) {
56776             for (var c =0;c < 7;c++) {
56777                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
56778             }    
56779         }
56780         
56781         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56782         for(i=0;i<cells.length;i++) {
56783             
56784             this.cells.elements[i].dayName = cells[i].dayName ;
56785             this.cells.elements[i].className = cells[i].className;
56786             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
56787             this.cells.elements[i].title = cells[i].title ;
56788             this.cells.elements[i].dateValue = cells[i].dateValue ;
56789         }
56790         
56791         
56792         
56793         
56794         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
56795         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
56796         
56797         ////if(totalRows != 6){
56798             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
56799            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
56800        // }
56801         
56802         this.fireEvent('monthchange', this, date);
56803         
56804         
56805     },
56806  /**
56807      * Returns the grid's SelectionModel.
56808      * @return {SelectionModel}
56809      */
56810     getSelectionModel : function(){
56811         if(!this.selModel){
56812             this.selModel = new Roo.grid.CellSelectionModel();
56813         }
56814         return this.selModel;
56815     },
56816
56817     load: function() {
56818         this.eventStore.load()
56819         
56820         
56821         
56822     },
56823     
56824     findCell : function(dt) {
56825         dt = dt.clearTime().getTime();
56826         var ret = false;
56827         this.cells.each(function(c){
56828             //Roo.log("check " +c.dateValue + '?=' + dt);
56829             if(c.dateValue == dt){
56830                 ret = c;
56831                 return false;
56832             }
56833             return true;
56834         });
56835         
56836         return ret;
56837     },
56838     
56839     findCells : function(rec) {
56840         var s = rec.data.start_dt.clone().clearTime().getTime();
56841        // Roo.log(s);
56842         var e= rec.data.end_dt.clone().clearTime().getTime();
56843        // Roo.log(e);
56844         var ret = [];
56845         this.cells.each(function(c){
56846              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
56847             
56848             if(c.dateValue > e){
56849                 return ;
56850             }
56851             if(c.dateValue < s){
56852                 return ;
56853             }
56854             ret.push(c);
56855         });
56856         
56857         return ret;    
56858     },
56859     
56860     findBestRow: function(cells)
56861     {
56862         var ret = 0;
56863         
56864         for (var i =0 ; i < cells.length;i++) {
56865             ret  = Math.max(cells[i].rows || 0,ret);
56866         }
56867         return ret;
56868         
56869     },
56870     
56871     
56872     addItem : function(rec)
56873     {
56874         // look for vertical location slot in
56875         var cells = this.findCells(rec);
56876         
56877         rec.row = this.findBestRow(cells);
56878         
56879         // work out the location.
56880         
56881         var crow = false;
56882         var rows = [];
56883         for(var i =0; i < cells.length; i++) {
56884             if (!crow) {
56885                 crow = {
56886                     start : cells[i],
56887                     end :  cells[i]
56888                 };
56889                 continue;
56890             }
56891             if (crow.start.getY() == cells[i].getY()) {
56892                 // on same row.
56893                 crow.end = cells[i];
56894                 continue;
56895             }
56896             // different row.
56897             rows.push(crow);
56898             crow = {
56899                 start: cells[i],
56900                 end : cells[i]
56901             };
56902             
56903         }
56904         
56905         rows.push(crow);
56906         rec.els = [];
56907         rec.rows = rows;
56908         rec.cells = cells;
56909         for (var i = 0; i < cells.length;i++) {
56910             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
56911             
56912         }
56913         
56914         
56915     },
56916     
56917     clearEvents: function() {
56918         
56919         if (!this.eventStore.getCount()) {
56920             return;
56921         }
56922         // reset number of rows in cells.
56923         Roo.each(this.cells.elements, function(c){
56924             c.rows = 0;
56925         });
56926         
56927         this.eventStore.each(function(e) {
56928             this.clearEvent(e);
56929         },this);
56930         
56931     },
56932     
56933     clearEvent : function(ev)
56934     {
56935         if (ev.els) {
56936             Roo.each(ev.els, function(el) {
56937                 el.un('mouseenter' ,this.onEventEnter, this);
56938                 el.un('mouseleave' ,this.onEventLeave, this);
56939                 el.remove();
56940             },this);
56941             ev.els = [];
56942         }
56943     },
56944     
56945     
56946     renderEvent : function(ev,ctr) {
56947         if (!ctr) {
56948              ctr = this.view.el.select('.fc-event-container',true).first();
56949         }
56950         
56951          
56952         this.clearEvent(ev);
56953             //code
56954        
56955         
56956         
56957         ev.els = [];
56958         var cells = ev.cells;
56959         var rows = ev.rows;
56960         this.fireEvent('eventrender', this, ev);
56961         
56962         for(var i =0; i < rows.length; i++) {
56963             
56964             cls = '';
56965             if (i == 0) {
56966                 cls += ' fc-event-start';
56967             }
56968             if ((i+1) == rows.length) {
56969                 cls += ' fc-event-end';
56970             }
56971             
56972             //Roo.log(ev.data);
56973             // how many rows should it span..
56974             var cg = this.eventTmpl.append(ctr,Roo.apply({
56975                 fccls : cls
56976                 
56977             }, ev.data) , true);
56978             
56979             
56980             cg.on('mouseenter' ,this.onEventEnter, this, ev);
56981             cg.on('mouseleave' ,this.onEventLeave, this, ev);
56982             cg.on('click', this.onEventClick, this, ev);
56983             
56984             ev.els.push(cg);
56985             
56986             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
56987             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
56988             //Roo.log(cg);
56989              
56990             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
56991             cg.setWidth(ebox.right - sbox.x -2);
56992         }
56993     },
56994     
56995     renderEvents: function()
56996     {   
56997         // first make sure there is enough space..
56998         
56999         if (!this.eventTmpl) {
57000             this.eventTmpl = new Roo.Template(
57001                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57002                     '<div class="fc-event-inner">' +
57003                         '<span class="fc-event-time">{time}</span>' +
57004                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57005                     '</div>' +
57006                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57007                 '</div>'
57008             );
57009                 
57010         }
57011                
57012         
57013         
57014         this.cells.each(function(c) {
57015             //Roo.log(c.select('.fc-day-content div',true).first());
57016             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57017         });
57018         
57019         var ctr = this.view.el.select('.fc-event-container',true).first();
57020         
57021         var cls;
57022         this.eventStore.each(function(ev){
57023             
57024             this.renderEvent(ev);
57025              
57026              
57027         }, this);
57028         this.view.layout();
57029         
57030     },
57031     
57032     onEventEnter: function (e, el,event,d) {
57033         this.fireEvent('evententer', this, el, event);
57034     },
57035     
57036     onEventLeave: function (e, el,event,d) {
57037         this.fireEvent('eventleave', this, el, event);
57038     },
57039     
57040     onEventClick: function (e, el,event,d) {
57041         this.fireEvent('eventclick', this, el, event);
57042     },
57043     
57044     onMonthChange: function () {
57045         this.store.load();
57046     },
57047     
57048     onLoad: function () {
57049         
57050         //Roo.log('calendar onload');
57051 //         
57052         if(this.eventStore.getCount() > 0){
57053             
57054            
57055             
57056             this.eventStore.each(function(d){
57057                 
57058                 
57059                 // FIXME..
57060                 var add =   d.data;
57061                 if (typeof(add.end_dt) == 'undefined')  {
57062                     Roo.log("Missing End time in calendar data: ");
57063                     Roo.log(d);
57064                     return;
57065                 }
57066                 if (typeof(add.start_dt) == 'undefined')  {
57067                     Roo.log("Missing Start time in calendar data: ");
57068                     Roo.log(d);
57069                     return;
57070                 }
57071                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57072                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57073                 add.id = add.id || d.id;
57074                 add.title = add.title || '??';
57075                 
57076                 this.addItem(d);
57077                 
57078              
57079             },this);
57080         }
57081         
57082         this.renderEvents();
57083     }
57084     
57085
57086 });
57087 /*
57088  grid : {
57089                 xtype: 'Grid',
57090                 xns: Roo.grid,
57091                 listeners : {
57092                     render : function ()
57093                     {
57094                         _this.grid = this;
57095                         
57096                         if (!this.view.el.hasClass('course-timesheet')) {
57097                             this.view.el.addClass('course-timesheet');
57098                         }
57099                         if (this.tsStyle) {
57100                             this.ds.load({});
57101                             return; 
57102                         }
57103                         Roo.log('width');
57104                         Roo.log(_this.grid.view.el.getWidth());
57105                         
57106                         
57107                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57108                             '.course-timesheet .x-grid-row' : {
57109                                 height: '80px'
57110                             },
57111                             '.x-grid-row td' : {
57112                                 'vertical-align' : 0
57113                             },
57114                             '.course-edit-link' : {
57115                                 'color' : 'blue',
57116                                 'text-overflow' : 'ellipsis',
57117                                 'overflow' : 'hidden',
57118                                 'white-space' : 'nowrap',
57119                                 'cursor' : 'pointer'
57120                             },
57121                             '.sub-link' : {
57122                                 'color' : 'green'
57123                             },
57124                             '.de-act-sup-link' : {
57125                                 'color' : 'purple',
57126                                 'text-decoration' : 'line-through'
57127                             },
57128                             '.de-act-link' : {
57129                                 'color' : 'red',
57130                                 'text-decoration' : 'line-through'
57131                             },
57132                             '.course-timesheet .course-highlight' : {
57133                                 'border-top-style': 'dashed !important',
57134                                 'border-bottom-bottom': 'dashed !important'
57135                             },
57136                             '.course-timesheet .course-item' : {
57137                                 'font-family'   : 'tahoma, arial, helvetica',
57138                                 'font-size'     : '11px',
57139                                 'overflow'      : 'hidden',
57140                                 'padding-left'  : '10px',
57141                                 'padding-right' : '10px',
57142                                 'padding-top' : '10px' 
57143                             }
57144                             
57145                         }, Roo.id());
57146                                 this.ds.load({});
57147                     }
57148                 },
57149                 autoWidth : true,
57150                 monitorWindowResize : false,
57151                 cellrenderer : function(v,x,r)
57152                 {
57153                     return v;
57154                 },
57155                 sm : {
57156                     xtype: 'CellSelectionModel',
57157                     xns: Roo.grid
57158                 },
57159                 dataSource : {
57160                     xtype: 'Store',
57161                     xns: Roo.data,
57162                     listeners : {
57163                         beforeload : function (_self, options)
57164                         {
57165                             options.params = options.params || {};
57166                             options.params._month = _this.monthField.getValue();
57167                             options.params.limit = 9999;
57168                             options.params['sort'] = 'when_dt';    
57169                             options.params['dir'] = 'ASC';    
57170                             this.proxy.loadResponse = this.loadResponse;
57171                             Roo.log("load?");
57172                             //this.addColumns();
57173                         },
57174                         load : function (_self, records, options)
57175                         {
57176                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57177                                 // if you click on the translation.. you can edit it...
57178                                 var el = Roo.get(this);
57179                                 var id = el.dom.getAttribute('data-id');
57180                                 var d = el.dom.getAttribute('data-date');
57181                                 var t = el.dom.getAttribute('data-time');
57182                                 //var id = this.child('span').dom.textContent;
57183                                 
57184                                 //Roo.log(this);
57185                                 Pman.Dialog.CourseCalendar.show({
57186                                     id : id,
57187                                     when_d : d,
57188                                     when_t : t,
57189                                     productitem_active : id ? 1 : 0
57190                                 }, function() {
57191                                     _this.grid.ds.load({});
57192                                 });
57193                            
57194                            });
57195                            
57196                            _this.panel.fireEvent('resize', [ '', '' ]);
57197                         }
57198                     },
57199                     loadResponse : function(o, success, response){
57200                             // this is overridden on before load..
57201                             
57202                             Roo.log("our code?");       
57203                             //Roo.log(success);
57204                             //Roo.log(response)
57205                             delete this.activeRequest;
57206                             if(!success){
57207                                 this.fireEvent("loadexception", this, o, response);
57208                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57209                                 return;
57210                             }
57211                             var result;
57212                             try {
57213                                 result = o.reader.read(response);
57214                             }catch(e){
57215                                 Roo.log("load exception?");
57216                                 this.fireEvent("loadexception", this, o, response, e);
57217                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57218                                 return;
57219                             }
57220                             Roo.log("ready...");        
57221                             // loop through result.records;
57222                             // and set this.tdate[date] = [] << array of records..
57223                             _this.tdata  = {};
57224                             Roo.each(result.records, function(r){
57225                                 //Roo.log(r.data);
57226                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57227                                     _this.tdata[r.data.when_dt.format('j')] = [];
57228                                 }
57229                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57230                             });
57231                             
57232                             //Roo.log(_this.tdata);
57233                             
57234                             result.records = [];
57235                             result.totalRecords = 6;
57236                     
57237                             // let's generate some duumy records for the rows.
57238                             //var st = _this.dateField.getValue();
57239                             
57240                             // work out monday..
57241                             //st = st.add(Date.DAY, -1 * st.format('w'));
57242                             
57243                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57244                             
57245                             var firstOfMonth = date.getFirstDayOfMonth();
57246                             var days = date.getDaysInMonth();
57247                             var d = 1;
57248                             var firstAdded = false;
57249                             for (var i = 0; i < result.totalRecords ; i++) {
57250                                 //var d= st.add(Date.DAY, i);
57251                                 var row = {};
57252                                 var added = 0;
57253                                 for(var w = 0 ; w < 7 ; w++){
57254                                     if(!firstAdded && firstOfMonth != w){
57255                                         continue;
57256                                     }
57257                                     if(d > days){
57258                                         continue;
57259                                     }
57260                                     firstAdded = true;
57261                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57262                                     row['weekday'+w] = String.format(
57263                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57264                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57265                                                     d,
57266                                                     date.format('Y-m-')+dd
57267                                                 );
57268                                     added++;
57269                                     if(typeof(_this.tdata[d]) != 'undefined'){
57270                                         Roo.each(_this.tdata[d], function(r){
57271                                             var is_sub = '';
57272                                             var deactive = '';
57273                                             var id = r.id;
57274                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57275                                             if(r.parent_id*1>0){
57276                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57277                                                 id = r.parent_id;
57278                                             }
57279                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57280                                                 deactive = 'de-act-link';
57281                                             }
57282                                             
57283                                             row['weekday'+w] += String.format(
57284                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57285                                                     id, //0
57286                                                     r.product_id_name, //1
57287                                                     r.when_dt.format('h:ia'), //2
57288                                                     is_sub, //3
57289                                                     deactive, //4
57290                                                     desc // 5
57291                                             );
57292                                         });
57293                                     }
57294                                     d++;
57295                                 }
57296                                 
57297                                 // only do this if something added..
57298                                 if(added > 0){ 
57299                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57300                                 }
57301                                 
57302                                 
57303                                 // push it twice. (second one with an hour..
57304                                 
57305                             }
57306                             //Roo.log(result);
57307                             this.fireEvent("load", this, o, o.request.arg);
57308                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57309                         },
57310                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57311                     proxy : {
57312                         xtype: 'HttpProxy',
57313                         xns: Roo.data,
57314                         method : 'GET',
57315                         url : baseURL + '/Roo/Shop_course.php'
57316                     },
57317                     reader : {
57318                         xtype: 'JsonReader',
57319                         xns: Roo.data,
57320                         id : 'id',
57321                         fields : [
57322                             {
57323                                 'name': 'id',
57324                                 'type': 'int'
57325                             },
57326                             {
57327                                 'name': 'when_dt',
57328                                 'type': 'string'
57329                             },
57330                             {
57331                                 'name': 'end_dt',
57332                                 'type': 'string'
57333                             },
57334                             {
57335                                 'name': 'parent_id',
57336                                 'type': 'int'
57337                             },
57338                             {
57339                                 'name': 'product_id',
57340                                 'type': 'int'
57341                             },
57342                             {
57343                                 'name': 'productitem_id',
57344                                 'type': 'int'
57345                             },
57346                             {
57347                                 'name': 'guid',
57348                                 'type': 'int'
57349                             }
57350                         ]
57351                     }
57352                 },
57353                 toolbar : {
57354                     xtype: 'Toolbar',
57355                     xns: Roo,
57356                     items : [
57357                         {
57358                             xtype: 'Button',
57359                             xns: Roo.Toolbar,
57360                             listeners : {
57361                                 click : function (_self, e)
57362                                 {
57363                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57364                                     sd.setMonth(sd.getMonth()-1);
57365                                     _this.monthField.setValue(sd.format('Y-m-d'));
57366                                     _this.grid.ds.load({});
57367                                 }
57368                             },
57369                             text : "Back"
57370                         },
57371                         {
57372                             xtype: 'Separator',
57373                             xns: Roo.Toolbar
57374                         },
57375                         {
57376                             xtype: 'MonthField',
57377                             xns: Roo.form,
57378                             listeners : {
57379                                 render : function (_self)
57380                                 {
57381                                     _this.monthField = _self;
57382                                    // _this.monthField.set  today
57383                                 },
57384                                 select : function (combo, date)
57385                                 {
57386                                     _this.grid.ds.load({});
57387                                 }
57388                             },
57389                             value : (function() { return new Date(); })()
57390                         },
57391                         {
57392                             xtype: 'Separator',
57393                             xns: Roo.Toolbar
57394                         },
57395                         {
57396                             xtype: 'TextItem',
57397                             xns: Roo.Toolbar,
57398                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57399                         },
57400                         {
57401                             xtype: 'Fill',
57402                             xns: Roo.Toolbar
57403                         },
57404                         {
57405                             xtype: 'Button',
57406                             xns: Roo.Toolbar,
57407                             listeners : {
57408                                 click : function (_self, e)
57409                                 {
57410                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57411                                     sd.setMonth(sd.getMonth()+1);
57412                                     _this.monthField.setValue(sd.format('Y-m-d'));
57413                                     _this.grid.ds.load({});
57414                                 }
57415                             },
57416                             text : "Next"
57417                         }
57418                     ]
57419                 },
57420                  
57421             }
57422         };
57423         
57424         *//*
57425  * Based on:
57426  * Ext JS Library 1.1.1
57427  * Copyright(c) 2006-2007, Ext JS, LLC.
57428  *
57429  * Originally Released Under LGPL - original licence link has changed is not relivant.
57430  *
57431  * Fork - LGPL
57432  * <script type="text/javascript">
57433  */
57434  
57435 /**
57436  * @class Roo.LoadMask
57437  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57438  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57439  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57440  * element's UpdateManager load indicator and will be destroyed after the initial load.
57441  * @constructor
57442  * Create a new LoadMask
57443  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57444  * @param {Object} config The config object
57445  */
57446 Roo.LoadMask = function(el, config){
57447     this.el = Roo.get(el);
57448     Roo.apply(this, config);
57449     if(this.store){
57450         this.store.on('beforeload', this.onBeforeLoad, this);
57451         this.store.on('load', this.onLoad, this);
57452         this.store.on('loadexception', this.onLoadException, this);
57453         this.removeMask = false;
57454     }else{
57455         var um = this.el.getUpdateManager();
57456         um.showLoadIndicator = false; // disable the default indicator
57457         um.on('beforeupdate', this.onBeforeLoad, this);
57458         um.on('update', this.onLoad, this);
57459         um.on('failure', this.onLoad, this);
57460         this.removeMask = true;
57461     }
57462 };
57463
57464 Roo.LoadMask.prototype = {
57465     /**
57466      * @cfg {Boolean} removeMask
57467      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57468      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57469      */
57470     /**
57471      * @cfg {String} msg
57472      * The text to display in a centered loading message box (defaults to 'Loading...')
57473      */
57474     msg : 'Loading...',
57475     /**
57476      * @cfg {String} msgCls
57477      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57478      */
57479     msgCls : 'x-mask-loading',
57480
57481     /**
57482      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57483      * @type Boolean
57484      */
57485     disabled: false,
57486
57487     /**
57488      * Disables the mask to prevent it from being displayed
57489      */
57490     disable : function(){
57491        this.disabled = true;
57492     },
57493
57494     /**
57495      * Enables the mask so that it can be displayed
57496      */
57497     enable : function(){
57498         this.disabled = false;
57499     },
57500     
57501     onLoadException : function()
57502     {
57503         Roo.log(arguments);
57504         
57505         if (typeof(arguments[3]) != 'undefined') {
57506             Roo.MessageBox.alert("Error loading",arguments[3]);
57507         } 
57508         /*
57509         try {
57510             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57511                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57512             }   
57513         } catch(e) {
57514             
57515         }
57516         */
57517     
57518         
57519         
57520         this.el.unmask(this.removeMask);
57521     },
57522     // private
57523     onLoad : function()
57524     {
57525         this.el.unmask(this.removeMask);
57526     },
57527
57528     // private
57529     onBeforeLoad : function(){
57530         if(!this.disabled){
57531             this.el.mask(this.msg, this.msgCls);
57532         }
57533     },
57534
57535     // private
57536     destroy : function(){
57537         if(this.store){
57538             this.store.un('beforeload', this.onBeforeLoad, this);
57539             this.store.un('load', this.onLoad, this);
57540             this.store.un('loadexception', this.onLoadException, this);
57541         }else{
57542             var um = this.el.getUpdateManager();
57543             um.un('beforeupdate', this.onBeforeLoad, this);
57544             um.un('update', this.onLoad, this);
57545             um.un('failure', this.onLoad, this);
57546         }
57547     }
57548 };/*
57549  * Based on:
57550  * Ext JS Library 1.1.1
57551  * Copyright(c) 2006-2007, Ext JS, LLC.
57552  *
57553  * Originally Released Under LGPL - original licence link has changed is not relivant.
57554  *
57555  * Fork - LGPL
57556  * <script type="text/javascript">
57557  */
57558
57559
57560 /**
57561  * @class Roo.XTemplate
57562  * @extends Roo.Template
57563  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57564 <pre><code>
57565 var t = new Roo.XTemplate(
57566         '&lt;select name="{name}"&gt;',
57567                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57568         '&lt;/select&gt;'
57569 );
57570  
57571 // then append, applying the master template values
57572  </code></pre>
57573  *
57574  * Supported features:
57575  *
57576  *  Tags:
57577
57578 <pre><code>
57579       {a_variable} - output encoded.
57580       {a_variable.format:("Y-m-d")} - call a method on the variable
57581       {a_variable:raw} - unencoded output
57582       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57583       {a_variable:this.method_on_template(...)} - call a method on the template object.
57584  
57585 </code></pre>
57586  *  The tpl tag:
57587 <pre><code>
57588         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57589         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57590         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57591         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57592   
57593         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57594         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57595 </code></pre>
57596  *      
57597  */
57598 Roo.XTemplate = function()
57599 {
57600     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57601     if (this.html) {
57602         this.compile();
57603     }
57604 };
57605
57606
57607 Roo.extend(Roo.XTemplate, Roo.Template, {
57608
57609     /**
57610      * The various sub templates
57611      */
57612     tpls : false,
57613     /**
57614      *
57615      * basic tag replacing syntax
57616      * WORD:WORD()
57617      *
57618      * // you can fake an object call by doing this
57619      *  x.t:(test,tesT) 
57620      * 
57621      */
57622     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57623
57624     /**
57625      * compile the template
57626      *
57627      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57628      *
57629      */
57630     compile: function()
57631     {
57632         var s = this.html;
57633      
57634         s = ['<tpl>', s, '</tpl>'].join('');
57635     
57636         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57637             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57638             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57639             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57640             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57641             m,
57642             id     = 0,
57643             tpls   = [];
57644     
57645         while(true == !!(m = s.match(re))){
57646             var forMatch   = m[0].match(nameRe),
57647                 ifMatch   = m[0].match(ifRe),
57648                 execMatch   = m[0].match(execRe),
57649                 namedMatch   = m[0].match(namedRe),
57650                 
57651                 exp  = null, 
57652                 fn   = null,
57653                 exec = null,
57654                 name = forMatch && forMatch[1] ? forMatch[1] : '';
57655                 
57656             if (ifMatch) {
57657                 // if - puts fn into test..
57658                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
57659                 if(exp){
57660                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
57661                 }
57662             }
57663             
57664             if (execMatch) {
57665                 // exec - calls a function... returns empty if true is  returned.
57666                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
57667                 if(exp){
57668                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
57669                 }
57670             }
57671             
57672             
57673             if (name) {
57674                 // for = 
57675                 switch(name){
57676                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
57677                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
57678                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
57679                 }
57680             }
57681             var uid = namedMatch ? namedMatch[1] : id;
57682             
57683             
57684             tpls.push({
57685                 id:     namedMatch ? namedMatch[1] : id,
57686                 target: name,
57687                 exec:   exec,
57688                 test:   fn,
57689                 body:   m[1] || ''
57690             });
57691             if (namedMatch) {
57692                 s = s.replace(m[0], '');
57693             } else { 
57694                 s = s.replace(m[0], '{xtpl'+ id + '}');
57695             }
57696             ++id;
57697         }
57698         this.tpls = [];
57699         for(var i = tpls.length-1; i >= 0; --i){
57700             this.compileTpl(tpls[i]);
57701             this.tpls[tpls[i].id] = tpls[i];
57702         }
57703         this.master = tpls[tpls.length-1];
57704         return this;
57705     },
57706     /**
57707      * same as applyTemplate, except it's done to one of the subTemplates
57708      * when using named templates, you can do:
57709      *
57710      * var str = pl.applySubTemplate('your-name', values);
57711      *
57712      * 
57713      * @param {Number} id of the template
57714      * @param {Object} values to apply to template
57715      * @param {Object} parent (normaly the instance of this object)
57716      */
57717     applySubTemplate : function(id, values, parent)
57718     {
57719         
57720         
57721         var t = this.tpls[id];
57722         
57723         
57724         try { 
57725             if(t.test && !t.test.call(this, values, parent)){
57726                 return '';
57727             }
57728         } catch(e) {
57729             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
57730             Roo.log(e.toString());
57731             Roo.log(t.test);
57732             return ''
57733         }
57734         try { 
57735             
57736             if(t.exec && t.exec.call(this, values, parent)){
57737                 return '';
57738             }
57739         } catch(e) {
57740             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
57741             Roo.log(e.toString());
57742             Roo.log(t.exec);
57743             return ''
57744         }
57745         try {
57746             var vs = t.target ? t.target.call(this, values, parent) : values;
57747             parent = t.target ? values : parent;
57748             if(t.target && vs instanceof Array){
57749                 var buf = [];
57750                 for(var i = 0, len = vs.length; i < len; i++){
57751                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
57752                 }
57753                 return buf.join('');
57754             }
57755             return t.compiled.call(this, vs, parent);
57756         } catch (e) {
57757             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
57758             Roo.log(e.toString());
57759             Roo.log(t.compiled);
57760             return '';
57761         }
57762     },
57763
57764     compileTpl : function(tpl)
57765     {
57766         var fm = Roo.util.Format;
57767         var useF = this.disableFormats !== true;
57768         var sep = Roo.isGecko ? "+" : ",";
57769         var undef = function(str) {
57770             Roo.log("Property not found :"  + str);
57771             return '';
57772         };
57773         
57774         var fn = function(m, name, format, args)
57775         {
57776             //Roo.log(arguments);
57777             args = args ? args.replace(/\\'/g,"'") : args;
57778             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
57779             if (typeof(format) == 'undefined') {
57780                 format= 'htmlEncode';
57781             }
57782             if (format == 'raw' ) {
57783                 format = false;
57784             }
57785             
57786             if(name.substr(0, 4) == 'xtpl'){
57787                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
57788             }
57789             
57790             // build an array of options to determine if value is undefined..
57791             
57792             // basically get 'xxxx.yyyy' then do
57793             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
57794             //    (function () { Roo.log("Property not found"); return ''; })() :
57795             //    ......
57796             
57797             var udef_ar = [];
57798             var lookfor = '';
57799             Roo.each(name.split('.'), function(st) {
57800                 lookfor += (lookfor.length ? '.': '') + st;
57801                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
57802             });
57803             
57804             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
57805             
57806             
57807             if(format && useF){
57808                 
57809                 args = args ? ',' + args : "";
57810                  
57811                 if(format.substr(0, 5) != "this."){
57812                     format = "fm." + format + '(';
57813                 }else{
57814                     format = 'this.call("'+ format.substr(5) + '", ';
57815                     args = ", values";
57816                 }
57817                 
57818                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
57819             }
57820              
57821             if (args.length) {
57822                 // called with xxyx.yuu:(test,test)
57823                 // change to ()
57824                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
57825             }
57826             // raw.. - :raw modifier..
57827             return "'"+ sep + udef_st  + name + ")"+sep+"'";
57828             
57829         };
57830         var body;
57831         // branched to use + in gecko and [].join() in others
57832         if(Roo.isGecko){
57833             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
57834                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
57835                     "';};};";
57836         }else{
57837             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
57838             body.push(tpl.body.replace(/(\r\n|\n)/g,
57839                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
57840             body.push("'].join('');};};");
57841             body = body.join('');
57842         }
57843         
57844         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
57845        
57846         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
57847         eval(body);
57848         
57849         return this;
57850     },
57851
57852     applyTemplate : function(values){
57853         return this.master.compiled.call(this, values, {});
57854         //var s = this.subs;
57855     },
57856
57857     apply : function(){
57858         return this.applyTemplate.apply(this, arguments);
57859     }
57860
57861  });
57862
57863 Roo.XTemplate.from = function(el){
57864     el = Roo.getDom(el);
57865     return new Roo.XTemplate(el.value || el.innerHTML);
57866 };